lygia
/lighting
/raymarch
/volume
)Default raymarching renderer. Based on Sébastien Hillaire's paper "Physically Based Sky, Atmosphere & Cloud Rendering in Frostbite"
Dependencies:
lygia
/lighting
/raymarch
/map
.glsl
lygia
/lighting
/light
/attenuation
.glsl
lygia
/generative
/random
.glsl
lygia
/math
/const
.glsl
lygia
/lighting
/medium
/new
.glsl
Use:
<vec4> raymarchVolume(<vec3> rayOrigin, <vec3> rayDirection, <vec2> st, float minDist, <vec3> background)
#ifndef LIGHT_COLOR
#if defined(GLSLVIEWER)
#define LIGHT_COLOR u_lightColor
#else
#define LIGHT_COLOR vec3(0.5, 0.5, 0.5)
#endif
#endif
#ifndef LIGHT_INTENSITY
#define LIGHT_INTENSITY 1.0
#endif
#ifndef RAYMARCH_VOLUME_SAMPLES
#define RAYMARCH_VOLUME_SAMPLES 64
#endif
#ifndef RAYMARCH_VOLUME_SAMPLES_LIGHT
#define RAYMARCH_VOLUME_SAMPLES_LIGHT 32
#endif
#ifndef RAYMARCH_VOLUME_MAP_FNC
#define RAYMARCH_VOLUME_MAP_FNC raymarchVolumeMap
#endif
#ifndef RAYMARCH_VOLUME_DITHER
#define RAYMARCH_VOLUME_DITHER 0.1
#endif
#if !defined(FNC_RAYMARCH_VOLUME) && defined(RAYMARCH_VOLUME)
#define FNC_RAYMARCH_VOLUME
vec3 raymarchVolumeShadowTransmittance(vec3 position, vec3 rayDirectionL, float stepSizeL) {
vec3 transmittanceL = vec3(1.0, 1.0, 1.0);
float tL = 0.0;
for (int j = 0; j < RAYMARCH_VOLUME_SAMPLES_LIGHT; j++) {
vec3 positionL = position + rayDirectionL * tL;
#if defined(RAYMARCH_VOLUMETRIC_SHADOWS)
Material mat = RAYMARCH_MAP_FNC(positionL);
if (mat.sdf <= 0.0) {
return vec3(0.0, 0.0, 0.0);
}
#endif
Medium resL = RAYMARCH_VOLUME_MAP_FNC(positionL);
float densityL = -resL.sdf;
vec3 extinctionL = resL.absorption + resL.scattering;
transmittanceL *= exp(-densityL * extinctionL * stepSizeL);
float offset = random(position)*(stepSizeL*RAYMARCH_VOLUME_DITHER);
tL += stepSizeL + offset;
}
return transmittanceL;
}
vec3 raymarchVolume(vec3 rayOrigin, vec3 rayDirection, vec2 st, float minDist, vec3 background) {
vec3 scatteredLuminance = vec3(0.0, 0.0, 0.0);
vec3 transmittance = vec3(1.0, 1.0, 1.0);
float stepSize = RAYMARCH_MAX_DIST/float(RAYMARCH_VOLUME_SAMPLES);
float t = RAYMARCH_MIN_DIST;
for(int i = 0; i < RAYMARCH_VOLUME_SAMPLES; i++) {
vec3 position = rayOrigin + rayDirection * t;
Medium res = RAYMARCH_VOLUME_MAP_FNC(position);
float density = -res.sdf;
vec3 extinction = res.absorption + res.scattering;
if (t < minDist && density > 0.0) {
#if defined(LIGHT_DIRECTION) || defined(LIGHT_POSITION)
#if defined(LIGHT_DIRECTION) // directional light
float stepSizeL = RAYMARCH_MAX_DIST/float(RAYMARCH_VOLUME_SAMPLES_LIGHT);
vec3 rayDirectionL = normalize(LIGHT_DIRECTION);
const float attenuationL = 1.0;
#else // point light
float distL = distance(LIGHT_POSITION, position);
float stepSizeL = distL/float(RAYMARCH_VOLUME_SAMPLES_LIGHT);
vec3 rayDirectionL = normalize(LIGHT_POSITION - position);
float attenuationL = attenuation(distL);
#endif
vec3 shadow = raymarchVolumeShadowTransmittance(position, rayDirectionL, stepSizeL);
vec3 L = LIGHT_COLOR * LIGHT_INTENSITY;
#else // no lighting
const float attenuationL = 1.0;
const vec3 shadow = 1.0;
const vec3 L = vec3(1.0, 1.0, 1.0);
#endif
#if defined(RAYMARCH_ENERGY_CONSERVING)
// energy-conserving scattering integration
vec3 S = L * attenuationL * shadow * density * res.scattering;
vec3 sampleExtinction = max(vec3(EPSILON, EPSILON, EPSILON), density * extinction);
vec3 Sint = (S - S * exp(-sampleExtinction * stepSize)) / sampleExtinction;
scatteredLuminance += transmittance * Sint;
transmittance *= exp(-sampleExtinction * stepSize);
#else
// usual scattering integration. Not energy-conserving.
scatteredLuminance += attenuationL * shadow * transmittance * density * res.scattering * stepSize * L;
transmittance *= exp(-density * extinction * stepSize);
#endif
}
float offset = random(st)*(stepSize*RAYMARCH_VOLUME_DITHER);
t += stepSize + offset;
}
return background * transmittance + scatteredLuminance;
}
#endif
Dependencies:
lygia
/lighting
/raymarch
/map
.glsl
lygia
/lighting
/light
/attenuation
.glsl
lygia
/generative
/random
.glsl
lygia
/math
/const
.glsl
lygia
/lighting
/medium
/new
.glsl
Use:
<float4> raymarchVolume(<float3> rayOrigin, <float3> rayDirection, <float2> st, <float> minDist, <float3> background)
#ifndef LIGHT_COLOR
#if defined(hlslVIEWER)
#define LIGHT_COLOR u_lightColor
#else
#define LIGHT_COLOR float3(0.5, 0.5, 0.5)
#endif
#endif
#ifndef LIGHT_INTENSITY
#define LIGHT_INTENSITY 1.0
#endif
#ifndef RAYMARCH_VOLUME_SAMPLES
#define RAYMARCH_VOLUME_SAMPLES 64
#endif
#ifndef RAYMARCH_VOLUME_SAMPLES_LIGHT
#define RAYMARCH_VOLUME_SAMPLES_LIGHT 32
#endif
#ifndef RAYMARCH_VOLUME_MAP_FNC
#define RAYMARCH_VOLUME_MAP_FNC raymarchVolumeMap
#endif
#ifndef RAYMARCH_VOLUME_DITHER
#define RAYMARCH_VOLUME_DITHER 0.1
#endif
#if !defined(FNC_RAYMARCH_VOLUME) && defined(RAYMARCH_VOLUME)
#define FNC_RAYMARCH_VOLUME
float3 raymarchVolumeShadowTransmittance(float3 position, float3 rayDirectionL, float stepSizeL) {
float3 transmittanceL = float3(1.0, 1.0, 1.0);
float tL = 0.0;
for (int j = 0; j < RAYMARCH_VOLUME_SAMPLES_LIGHT; j++) {
float3 positionL = position + rayDirectionL * tL;
#if defined(RAYMARCH_VOLUMETRIC_SHADOWS)
Material mat = RAYMARCH_MAP_FNC(positionL);
if (mat.sdf <= 0.0) {
return float3(0.0, 0.0, 0.0);
}
#endif
Medium resL = RAYMARCH_VOLUME_MAP_FNC(positionL);
float densityL = -resL.sdf;
float3 extinctionL = resL.absorption + resL.scattering;
transmittanceL *= exp(-densityL * extinctionL * stepSizeL);
float offset = random(position)*(stepSizeL*RAYMARCH_VOLUME_DITHER);
tL += stepSizeL + offset;
}
return transmittanceL;
}
float3 raymarchVolume(in float3 rayOrigin, in float3 rayDirection, float2 st, float minDist, float3 background) {
float3 scatteredLuminance = float3(0.0, 0.0, 0.0);
float3 transmittance = float3(1.0, 1.0, 1.0);
float stepSize = RAYMARCH_MAX_DIST/float(RAYMARCH_VOLUME_SAMPLES);
float t = RAYMARCH_MIN_DIST;
for(int i = 0; i < RAYMARCH_VOLUME_SAMPLES; i++) {
float3 position = rayOrigin + rayDirection * t;
Medium res = RAYMARCH_VOLUME_MAP_FNC(position);
float density = -res.sdf;
float3 extinction = res.absorption + res.scattering;
if (t < minDist && density > 0.0) {
#if defined(LIGHT_DIRECTION) || defined(LIGHT_POSITION)
#if defined(LIGHT_DIRECTION) // directional light
float stepSizeL = RAYMARCH_MAX_DIST/float(RAYMARCH_VOLUME_SAMPLES_LIGHT);
float3 rayDirectionL = LIGHT_DIRECTION;
const float attenuationL = 1.0;
#else // point light
float distL = distance(LIGHT_POSITION, position);
float stepSizeL = distL/float(RAYMARCH_VOLUME_SAMPLES_LIGHT);
float3 rayDirectionL = normalize(LIGHT_POSITION - position);
float attenuationL = attenuation(distL);
#endif
float3 shadow = raymarchVolumeShadowTransmittance(position, rayDirectionL, stepSizeL);
float3 L = LIGHT_COLOR * LIGHT_INTENSITY;
#else // no lighting
const float attenuationL = 1.0;
const float3 shadow = 1.0;
const float3 L = float3(1.0, 1.0, 1.0);
#endif
#if defined(RAYMARCH_ENERGY_CONSERVING)
// energy-conserving scattering integration
float3 S = L * attenuationL * shadow * density * res.scattering;
float3 sampleExtinction = max(float3(EPSILON, EPSILON, EPSILON), density * extinction);
float3 Sint = (S - S * exp(-sampleExtinction * stepSize)) / sampleExtinction;
scatteredLuminance += transmittance * Sint;
transmittance *= exp(-sampleExtinction * stepSize);
#else
// usual scattering integration. Not energy-conserving.
scatteredLuminance += attenuationL * shadow * transmittance * density * res.scattering * stepSize * L;
transmittance *= exp(-density * extinction * stepSize);
#endif
}
float offset = random(st)*(stepSize*RAYMARCH_VOLUME_DITHER);
t += stepSize + offset;
}
return background * transmittance + scatteredLuminance;
}
#endif
MIT License (MIT) Copyright (c) 2024 Shadi EL Hajj
LYGIA is dual-licensed under the Prosperity License and the Patron License for sponsors and contributors.
Sponsors and contributors are automatically added to the Patron License and they can ignore the any non-commercial rule of the Prosperity Licensed software (please take a look to the exception).
It's also possible to get a permanent comercial license hook to a single and specific version of LYGIA.
Sign up for the news letter bellow, joing the LYGIA's channel on Discord or follow the Github repository