diff --git a/Shaders/std/sky.glsl b/Shaders/std/sky.glsl index 5a58734f..6fca94dd 100644 --- a/Shaders/std/sky.glsl +++ b/Shaders/std/sky.glsl @@ -21,6 +21,7 @@ #define _SKY_GLSL_ uniform sampler2D nishitaLUT; +uniform vec2 nishitaDensity; #ifndef PI #define PI 3.141592 @@ -91,9 +92,8 @@ vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) { * r0: ray origin * pSun: normalized sun direction * rPlanet: planet radius - * density: (air density, dust density, ozone density) */ -vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet, const vec3 density) { +vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) { // Calculate the step size of the primary ray. vec2 p = nishita_rsi(r0, r, nishita_atmo_radius); if (p.x > p.y) return vec3(0,0,0); @@ -127,8 +127,8 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa float iHeight = length(iPos) - rPlanet; // Calculate the optical depth of the Rayleigh and Mie scattering for this step - float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * density.x * iStepSize; - float odStepMie = exp(-iHeight / nishita_mie_scale) * density.y * iStepSize; + float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * nishitaDensity.x * iStepSize; + float odStepMie = exp(-iHeight / nishita_mie_scale) * nishitaDensity.y * iStepSize; // Accumulate optical depth. iOdRlh += odStepRlh; diff --git a/Sources/armory/object/Uniforms.hx b/Sources/armory/object/Uniforms.hx index e87f809b..9416e79c 100644 --- a/Sources/armory/object/Uniforms.hx +++ b/Sources/armory/object/Uniforms.hx @@ -11,14 +11,14 @@ class Uniforms { public static function register() { iron.object.Uniforms.externalTextureLinks = [textureLink]; - iron.object.Uniforms.externalVec2Links = []; + iron.object.Uniforms.externalVec2Links = [vec2Link]; iron.object.Uniforms.externalVec3Links = [vec3Link]; iron.object.Uniforms.externalVec4Links = []; iron.object.Uniforms.externalFloatLinks = [floatLink]; iron.object.Uniforms.externalIntLinks = []; } - public static function textureLink(object: Object, mat: MaterialData, link: String): kha.Image { + public static function textureLink(object: Object, mat: MaterialData, link: String): Null { switch (link) { case "_nishitaLUT": { if (armory.renderpath.Nishita.data == null) armory.renderpath.Nishita.recompute(Scene.active.world); @@ -40,7 +40,7 @@ class Uniforms { return target != null ? target.image : null; } - public static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 { + public static function vec3Link(object: Object, mat: MaterialData, link: String): Null { var v: Vec4 = null; switch (link) { #if arm_hosek @@ -173,6 +173,23 @@ class Uniforms { return v; } + public static function vec2Link(object: Object, mat: MaterialData, link: String): Null { + var v: Vec4 = null; + switch (link) { + case "_nishitaDensity": { + v = iron.object.Uniforms.helpVec; + var w = Scene.active.world; + if (w != null) { + // We only need Rayleigh and Mie density in the sky shader -> Vec2 + v.x = w.raw.nishita_density[0]; + v.y = w.raw.nishita_density[1]; + } + } + } + + return v; + } + public static function floatLink(object: Object, mat: MaterialData, link: String): Null { switch (link) { #if rp_dynres diff --git a/Sources/armory/renderpath/Nishita.hx b/Sources/armory/renderpath/Nishita.hx index 1a4cac92..7dcba4a2 100644 --- a/Sources/armory/renderpath/Nishita.hx +++ b/Sources/armory/renderpath/Nishita.hx @@ -1,6 +1,7 @@ package armory.renderpath; import kha.FastFloat; +import kha.arrays.Float32Array; import kha.graphics4.TextureFormat; import kha.graphics4.Usage; @@ -18,15 +19,28 @@ class Nishita { public static var data: NishitaData = null; /** - Recompute the nishita lookup table. Call this function after updating - the sky density settings. + Recomputes the nishita lookup table after the density settings changed. + Do not call this method on every frame (it's slow)! **/ public static function recompute(world: WorldData) { - if (world == null || world.raw.sun_direction == null) return; + if (world == null || world.raw.nishita_density == null) return; if (data == null) data = new NishitaData(); - // TODO - data.computeLUT(new Vec3(1.0, 1.0, 1.0)); + var density = world.raw.nishita_density; + data.computeLUT(new Vec3(density[0], density[1], density[2])); + } + + /** Sets the sky's density parameters and calls `recompute()` afterwards. **/ + public static function setDensity(world: WorldData, densityAir: FastFloat, densityDust: FastFloat, densityOzone: FastFloat) { + if (world == null) return; + + if (world.raw.nishita_density == null) world.raw.nishita_density = new Float32Array(3); + var density = world.raw.nishita_density; + density[0] = Helper.clamp(densityAir, 0, 10); + density[1] = Helper.clamp(densityDust, 0, 10); + density[2] = Helper.clamp(densityOzone, 0, 10); + + recompute(world); } } diff --git a/blender/arm/exporter.py b/blender/arm/exporter.py index f58039cf..e3841a28 100755 --- a/blender/arm/exporter.py +++ b/blender/arm/exporter.py @@ -2843,6 +2843,7 @@ class ArmoryExporter: out_world['sun_direction'] = list(world.arm_envtex_sun_direction) out_world['turbidity'] = world.arm_envtex_turbidity out_world['ground_albedo'] = world.arm_envtex_ground_albedo + out_world['nishita_density'] = list(world.arm_nishita_density) disable_hdr = world.arm_envtex_name.endswith('.jpg') diff --git a/blender/arm/material/cycles_nodes/nodes_texture.py b/blender/arm/material/cycles_nodes/nodes_texture.py index c88a8373..f9c35dd8 100644 --- a/blender/arm/material/cycles_nodes/nodes_texture.py +++ b/blender/arm/material/cycles_nodes/nodes_texture.py @@ -375,11 +375,12 @@ def parse_sky_nishita(node: bpy.types.ShaderNodeTexSky, state: ParserState) -> v curshader.add_uniform('vec3 sunDir', link='_sunDirection') curshader.add_uniform('sampler2D nishitaLUT', link='_nishitaLUT', included=True, tex_addr_u='clamp', tex_addr_v='clamp') + curshader.add_uniform('vec2 nishitaDensity', link='_nishitaDensity', included=True) planet_radius = 6360e3 # Earth radius used in Blender ray_origin_z = planet_radius + node.altitude - density = c.to_vec3((node.air_density, node.dust_density, node.ozone_density)) + state.world.arm_nishita_density = [node.air_density, node.dust_density, node.ozone_density] sun = '' if node.sun_disc: @@ -399,7 +400,7 @@ def parse_sky_nishita(node: bpy.types.ShaderNodeTexSky, state: ParserState) -> v size = math.cos(theta) sun = f'* sun_disk(n, sunDir, {size}, {node.sun_intensity})' - return f'nishita_atmosphere(n, vec3(0, 0, {ray_origin_z}), sunDir, {planet_radius}, {density}){sun}' + return f'nishita_atmosphere(n, vec3(0, 0, {ray_origin_z}), sunDir, {planet_radius}){sun}' def parse_tex_environment(node: bpy.types.ShaderNodeTexEnvironment, out_socket: bpy.types.NodeSocket, state: ParserState) -> vec3str: diff --git a/blender/arm/props.py b/blender/arm/props.py index 1c5b1c5b..b286cd5e 100755 --- a/blender/arm/props.py +++ b/blender/arm/props.py @@ -326,6 +326,7 @@ def init_properties(): bpy.types.World.arm_envtex_sun_direction = FloatVectorProperty(name="Sun Direction", size=3, default=[0,0,0]) bpy.types.World.arm_envtex_turbidity = FloatProperty(name="Turbidity", default=1.0) bpy.types.World.arm_envtex_ground_albedo = FloatProperty(name="Ground Albedo", default=0.0) + bpy.types.World.arm_nishita_density = FloatVectorProperty(name="Nishita Density", size=3, default=[1, 1, 1]) bpy.types.Material.arm_cast_shadow = BoolProperty(name="Cast Shadow", default=True) bpy.types.Material.arm_receive_shadow = BoolProperty(name="Receive Shadow", description="Requires forward render path", default=True) bpy.types.Material.arm_overlay = BoolProperty(name="Overlay", default=False)