forked from LeenkxTeam/LNXSDK
Update
This commit is contained in:
@ -348,7 +348,13 @@ typedef TWorldData = {
|
||||
@:optional public var turbidity: Null<FastFloat>;
|
||||
@:optional public var ground_albedo: Null<FastFloat>;
|
||||
@:optional public var envmap: String;
|
||||
@:optional public var nishita_density: Float32Array; // Rayleigh, Mie, ozone
|
||||
@:optional public var sky_density: Float32Array; // Air, dust/aerosol, ozone density
|
||||
@:optional public var sky_sun_elevation: Null<FastFloat>;
|
||||
@:optional public var sky_sun_rotation: Null<FastFloat>;
|
||||
@:optional public var sky_sun_size: Null<FastFloat>;
|
||||
@:optional public var sky_sun_intensity: Null<FastFloat>;
|
||||
@:optional public var sky_altitude: Null<FastFloat>;
|
||||
@:optional public var sky_sun_disc: Null<Int>; // 0 or 1
|
||||
}
|
||||
|
||||
#if js
|
||||
|
||||
@ -1,27 +0,0 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetHosekWilkiePropertiesNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
|
||||
var world = iron.Scene.active.world.raw;
|
||||
|
||||
return switch (from) {
|
||||
case 0:
|
||||
world.turbidity;
|
||||
case 1:
|
||||
world.ground_albedo;
|
||||
case 2:
|
||||
new Vec4(world.sun_direction[0], world.sun_direction[1], world.sun_direction[2]);
|
||||
default:
|
||||
null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetNishitaPropertiesNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
|
||||
var world = iron.Scene.active.world.raw;
|
||||
|
||||
return switch (from) {
|
||||
case 0:
|
||||
world.nishita_density[0];
|
||||
case 1:
|
||||
world.nishita_density[1];
|
||||
case 2:
|
||||
world.nishita_density[2];
|
||||
case 3:
|
||||
new Vec4(world.sun_direction[0], world.sun_direction[1], world.sun_direction[2]);
|
||||
default:
|
||||
null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
15
leenkx/Sources/leenkx/logicnode/GetWorldColorNode.hx
Normal file
15
leenkx/Sources/leenkx/logicnode/GetWorldColorNode.hx
Normal file
@ -0,0 +1,15 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetWorldColorNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var col = iron.Scene.active.world.raw.background_color;
|
||||
return new Vec4(((col >> 16) & 0xff) / 255, ((col >> 8) & 0xff) / 255, (col & 0xff) / 255, 1.0);
|
||||
}
|
||||
}
|
||||
50
leenkx/Sources/leenkx/logicnode/GetWorldSkyNode.hx
Normal file
50
leenkx/Sources/leenkx/logicnode/GetWorldSkyNode.hx
Normal file
@ -0,0 +1,50 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class GetWorldSkyNode extends LogicNode {
|
||||
|
||||
public var property0:String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var world = iron.Scene.active.world.raw;
|
||||
|
||||
if (property0 == 'hosek') {
|
||||
return switch (from) {
|
||||
case 0: world.turbidity != null ? world.turbidity : 1.0;
|
||||
case 1: world.ground_albedo != null ? world.ground_albedo : 0.0;
|
||||
case 2: world.sun_direction != null ? new Vec4(world.sun_direction[0], world.sun_direction[1], world.sun_direction[2]) : new Vec4(0, 0, 1);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
else if (property0 == 'single') {
|
||||
return switch (from) {
|
||||
case 0: world.sky_density != null ? world.sky_density[0] : 1.0;
|
||||
case 1: world.sky_density != null ? world.sky_density[1] : 1.0;
|
||||
case 2: world.sky_density != null ? world.sky_density[2] : 1.0;
|
||||
case 3: world.sky_altitude != null ? world.sky_altitude : 0.0;
|
||||
case 4: world.sun_direction != null ? new Vec4(world.sun_direction[0], world.sun_direction[1], world.sun_direction[2]) : new Vec4(0, 0, 1);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
else { // multi
|
||||
return switch (from) {
|
||||
case 0: world.sky_density != null ? world.sky_density[0] : 1.0;
|
||||
case 1: world.sky_density != null ? world.sky_density[1] : 1.0;
|
||||
case 2: world.sky_density != null ? world.sky_density[2] : 1.0;
|
||||
case 3: world.sky_sun_elevation != null ? world.sky_sun_elevation : 0.0;
|
||||
case 4: world.sky_sun_rotation != null ? world.sky_sun_rotation : 0.0;
|
||||
case 5: world.sky_sun_size != null ? world.sky_sun_size : 0.545;
|
||||
case 6: world.sky_sun_intensity != null ? world.sky_sun_intensity : 1.0;
|
||||
case 7: world.sky_altitude != null ? world.sky_altitude : 0.0;
|
||||
case 8: world.sky_sun_disc != null ? world.sky_sun_disc : 1;
|
||||
case 9: world.sun_direction != null ? new Vec4(world.sun_direction[0], world.sun_direction[1], world.sun_direction[2]) : new Vec4(0, 0, 1);
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
17
leenkx/Sources/leenkx/logicnode/GetWorldTextureNode.hx
Normal file
17
leenkx/Sources/leenkx/logicnode/GetWorldTextureNode.hx
Normal file
@ -0,0 +1,17 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class GetWorldTextureNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function get(from: Int): Dynamic {
|
||||
var world = iron.Scene.active.world;
|
||||
return switch (from) {
|
||||
case 0: world.probe != null ? world.probe.raw.strength : 1.0;
|
||||
case 1: world.raw.envmap != null ? world.raw.envmap : '';
|
||||
default: null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import leenkx.renderpath.HosekWilkie;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class SetHosekWilkiePropertiesNode extends LogicNode {
|
||||
|
||||
public var property0:String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
|
||||
var world = iron.Scene.active.world;
|
||||
|
||||
if(property0 == 'Turbidity/Ground Albedo'){
|
||||
world.raw.turbidity = inputs[1].get();
|
||||
world.raw.ground_albedo = inputs[2].get();
|
||||
}
|
||||
|
||||
if(property0 == 'Turbidity')
|
||||
world.raw.turbidity = inputs[1].get();
|
||||
|
||||
if(property0 == 'Ground Albedo')
|
||||
world.raw.ground_albedo = inputs[1].get();
|
||||
|
||||
if(property0 == 'Sun Direction'){
|
||||
var vec:Vec4 = inputs[1].get();
|
||||
world.raw.sun_direction[0] = vec.x;
|
||||
world.raw.sun_direction[1] = vec.y;
|
||||
world.raw.sun_direction[2] = vec.z;
|
||||
}
|
||||
|
||||
HosekWilkie.recompute(world);
|
||||
|
||||
runOutput(0);
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,45 +0,0 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import leenkx.renderpath.Nishita;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class SetNishitaPropertiesNode extends LogicNode {
|
||||
|
||||
public var property0:String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
|
||||
var world = iron.Scene.active.world;
|
||||
|
||||
if(property0 == 'Density'){
|
||||
world.raw.nishita_density[0] = inputs[1].get();
|
||||
world.raw.nishita_density[1] = inputs[2].get();
|
||||
world.raw.nishita_density[2] = inputs[3].get();
|
||||
}
|
||||
|
||||
if(property0 == 'Air')
|
||||
world.raw.nishita_density[0] = inputs[1].get();
|
||||
|
||||
if(property0 == 'Dust')
|
||||
world.raw.nishita_density[1] = inputs[1].get();
|
||||
|
||||
if(property0 == 'Ozone')
|
||||
world.raw.nishita_density[2] = inputs[1].get();
|
||||
|
||||
if(property0 == 'Sun Direction'){
|
||||
var vec:Vec4 = inputs[1].get();
|
||||
world.raw.sun_direction[0] = vec.x;
|
||||
world.raw.sun_direction[1] = vec.y;
|
||||
world.raw.sun_direction[2] = vec.z;
|
||||
}
|
||||
|
||||
Nishita.recompute(world);
|
||||
|
||||
runOutput(0);
|
||||
|
||||
}
|
||||
}
|
||||
23
leenkx/Sources/leenkx/logicnode/SetWorldColorNode.hx
Normal file
23
leenkx/Sources/leenkx/logicnode/SetWorldColorNode.hx
Normal file
@ -0,0 +1,23 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import iron.math.Vec4;
|
||||
|
||||
class SetWorldColorNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var world = iron.Scene.active.world;
|
||||
var raw = world.raw;
|
||||
|
||||
var vec:Vec4 = inputs[1].get();
|
||||
var r = Std.int(Math.max(0, Math.min(1, vec.x)) * 255);
|
||||
var g = Std.int(Math.max(0, Math.min(1, vec.y)) * 255);
|
||||
var b = Std.int(Math.max(0, Math.min(1, vec.z)) * 255);
|
||||
raw.background_color = (r << 16) | (g << 8) | b;
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
63
leenkx/Sources/leenkx/logicnode/SetWorldSkyNode.hx
Normal file
63
leenkx/Sources/leenkx/logicnode/SetWorldSkyNode.hx
Normal file
@ -0,0 +1,63 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
import leenkx.renderpath.Sky;
|
||||
import leenkx.renderpath.HosekWilkie;
|
||||
import iron.math.Vec4;
|
||||
|
||||
class SetWorldSkyNode extends LogicNode {
|
||||
|
||||
public var property0:String;
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var world = iron.Scene.active.world;
|
||||
var raw = world.raw;
|
||||
|
||||
if (property0 == 'hosek') {
|
||||
raw.turbidity = inputs[1].get();
|
||||
raw.ground_albedo = inputs[2].get();
|
||||
var vec:Vec4 = inputs[3].get();
|
||||
if (raw.sun_direction == null) raw.sun_direction = new kha.arrays.Float32Array(3);
|
||||
raw.sun_direction[0] = vec.x;
|
||||
raw.sun_direction[1] = vec.y;
|
||||
raw.sun_direction[2] = vec.z;
|
||||
HosekWilkie.recompute(world);
|
||||
}
|
||||
else if (property0 == 'single') {
|
||||
if (raw.sky_density == null) raw.sky_density = new kha.arrays.Float32Array(3);
|
||||
raw.sky_density[0] = inputs[1].get();
|
||||
raw.sky_density[1] = inputs[2].get();
|
||||
raw.sky_density[2] = inputs[3].get();
|
||||
raw.sky_altitude = inputs[4].get();
|
||||
var vec:Vec4 = inputs[5].get();
|
||||
if (raw.sun_direction == null) raw.sun_direction = new kha.arrays.Float32Array(3);
|
||||
raw.sun_direction[0] = vec.x;
|
||||
raw.sun_direction[1] = vec.y;
|
||||
raw.sun_direction[2] = vec.z;
|
||||
Sky.recomputeSingleScatter(world);
|
||||
}
|
||||
else if (property0 == 'multi') {
|
||||
if (raw.sky_density == null) raw.sky_density = new kha.arrays.Float32Array(3);
|
||||
raw.sky_density[0] = inputs[1].get();
|
||||
raw.sky_density[1] = inputs[2].get();
|
||||
raw.sky_density[2] = inputs[3].get();
|
||||
raw.sky_sun_elevation = inputs[4].get();
|
||||
raw.sky_sun_rotation = inputs[5].get();
|
||||
raw.sky_sun_size = inputs[6].get();
|
||||
raw.sky_sun_intensity = inputs[7].get();
|
||||
raw.sky_altitude = inputs[8].get();
|
||||
raw.sky_sun_disc = inputs[9].get() ? 1 : 0;
|
||||
var vec:Vec4 = inputs[10].get();
|
||||
if (raw.sun_direction == null) raw.sun_direction = new kha.arrays.Float32Array(3);
|
||||
raw.sun_direction[0] = vec.x;
|
||||
raw.sun_direction[1] = vec.y;
|
||||
raw.sun_direction[2] = vec.z;
|
||||
Sky.recomputeMultiScatter(world);
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
25
leenkx/Sources/leenkx/logicnode/SetWorldTextureNode.hx
Normal file
25
leenkx/Sources/leenkx/logicnode/SetWorldTextureNode.hx
Normal file
@ -0,0 +1,25 @@
|
||||
package leenkx.logicnode;
|
||||
|
||||
class SetWorldTextureNode extends LogicNode {
|
||||
|
||||
public function new(tree: LogicTree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
override function run(from: Int) {
|
||||
var world = iron.Scene.active.world;
|
||||
var raw = world.raw;
|
||||
|
||||
if (world.probe != null) {
|
||||
world.probe.raw.strength = inputs[1].get();
|
||||
}
|
||||
|
||||
var envmap:String = inputs[2].get();
|
||||
if (envmap != null && envmap != '' && envmap != raw.envmap) {
|
||||
raw.envmap = envmap;
|
||||
world.loadEnvmap(function(w) {});
|
||||
}
|
||||
|
||||
runOutput(0);
|
||||
}
|
||||
}
|
||||
@ -18,7 +18,7 @@ class Uniforms {
|
||||
iron.object.Uniforms.externalTextureLinks = [textureLink];
|
||||
iron.object.Uniforms.externalVec2Links = [vec2Link];
|
||||
iron.object.Uniforms.externalVec3Links = [vec3Link];
|
||||
iron.object.Uniforms.externalVec4Links = [];
|
||||
iron.object.Uniforms.externalVec4Links = [vec4Link];
|
||||
iron.object.Uniforms.externalFloatLinks = [floatLink];
|
||||
iron.object.Uniforms.externalFloatsLinks = [floatsLink];
|
||||
iron.object.Uniforms.externalIntLinks = [intLink];
|
||||
@ -26,9 +26,13 @@ class Uniforms {
|
||||
|
||||
public static function textureLink(object: Object, mat: MaterialData, link: String): Null<kha.Image> {
|
||||
switch (link) {
|
||||
case "_nishitaLUT": {
|
||||
if (leenkx.renderpath.Nishita.data == null) leenkx.renderpath.Nishita.recompute(Scene.active.world);
|
||||
return leenkx.renderpath.Nishita.data.lut;
|
||||
case "_singleScatterLUT": {
|
||||
if (leenkx.renderpath.Sky.singleScatterData == null) leenkx.renderpath.Sky.recomputeSingleScatter(Scene.active.world);
|
||||
return leenkx.renderpath.Sky.singleScatterData.lut;
|
||||
}
|
||||
case "_multiScatterLUT": {
|
||||
if (leenkx.renderpath.Sky.multiScatterData == null) leenkx.renderpath.Sky.recomputeMultiScatter(Scene.active.world);
|
||||
return leenkx.renderpath.Sky.multiScatterData.lut;
|
||||
}
|
||||
#if lnx_ltc
|
||||
case "_ltcMat": {
|
||||
@ -169,6 +173,17 @@ class Uniforms {
|
||||
}
|
||||
}
|
||||
#end
|
||||
case "_multiScatterSunTop": {
|
||||
if (leenkx.renderpath.Sky.multiScatterData == null) {
|
||||
leenkx.renderpath.Sky.recomputeMultiScatter(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.Sky.multiScatterData != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.Sky.multiScatterData.sunTop.x;
|
||||
v.y = leenkx.renderpath.Sky.multiScatterData.sunTop.y;
|
||||
v.z = leenkx.renderpath.Sky.multiScatterData.sunTop.z;
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@ -176,13 +191,13 @@ class Uniforms {
|
||||
public static function vec2Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
switch (link) {
|
||||
case "_nishitaDensity": {
|
||||
case "_skyDensity": {
|
||||
var w = Scene.active.world;
|
||||
if (w != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
// 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];
|
||||
v.x = w.raw.sky_density[0];
|
||||
v.y = w.raw.sky_density[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -190,6 +205,35 @@ class Uniforms {
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function vec4Link(object: Object, mat: MaterialData, link: String): Null<iron.math.Vec4> {
|
||||
var v: Vec4 = null;
|
||||
switch (link) {
|
||||
case "_multiScatterParams": {
|
||||
var w = Scene.active.world;
|
||||
if (w != null && w.raw.sky_sun_elevation != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = w.raw.sky_sun_elevation;
|
||||
v.y = w.raw.sky_sun_rotation != null ? w.raw.sky_sun_rotation : 0.0;
|
||||
v.z = w.raw.sky_sun_size != null ? w.raw.sky_sun_size : 0.545;
|
||||
v.w = w.raw.sky_sun_intensity != null ? w.raw.sky_sun_intensity : 1.0;
|
||||
}
|
||||
}
|
||||
case "_multiScatterSunBottom": {
|
||||
if (leenkx.renderpath.Sky.multiScatterData == null) {
|
||||
leenkx.renderpath.Sky.recomputeMultiScatter(Scene.active.world);
|
||||
}
|
||||
if (leenkx.renderpath.Sky.multiScatterData != null) {
|
||||
v = iron.object.Uniforms.helpVec;
|
||||
v.x = leenkx.renderpath.Sky.multiScatterData.sunBottom.x;
|
||||
v.y = leenkx.renderpath.Sky.multiScatterData.sunBottom.y;
|
||||
v.z = leenkx.renderpath.Sky.multiScatterData.sunBottom.z;
|
||||
v.w = leenkx.renderpath.Sky.multiScatterData.earthIntersectionAngle;
|
||||
}
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
public static function floatLink(object: Object, mat: MaterialData, link: String): Null<kha.FastFloat> {
|
||||
switch (link) {
|
||||
#if rp_dynres
|
||||
|
||||
@ -8,50 +8,99 @@ import kha.graphics4.Usage;
|
||||
import iron.data.WorldData;
|
||||
import iron.math.Vec2;
|
||||
import iron.math.Vec3;
|
||||
import iron.math.Vec4;
|
||||
|
||||
import leenkx.math.Helper;
|
||||
|
||||
/**
|
||||
Utility class to control the Nishita sky model.
|
||||
Utility class to control the sky models (single and multiple scattering).
|
||||
**/
|
||||
class Nishita {
|
||||
class Sky {
|
||||
|
||||
public static var data: NishitaData = null;
|
||||
public static var singleScatterData: SingleScatteringData = null;
|
||||
public static var multiScatterData: MultipleScatteringData = null;
|
||||
|
||||
/**
|
||||
Recomputes the nishita lookup table after the density settings changed.
|
||||
Recomputes the single scattering 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.nishita_density == null) return;
|
||||
if (data == null) data = new NishitaData();
|
||||
public static function recomputeSingleScatter(world: WorldData) {
|
||||
if (world == null || world.raw.sky_density == null) return;
|
||||
if (singleScatterData == null) singleScatterData = new SingleScatteringData();
|
||||
|
||||
var density = world.raw.nishita_density;
|
||||
data.computeLUT(new Vec3(density[0], density[1], density[2]));
|
||||
var density = world.raw.sky_density;
|
||||
singleScatterData.computeLUT(new Vec3(density[0], density[1], density[2]));
|
||||
}
|
||||
|
||||
/** Sets the sky's density parameters and calls `recompute()` afterwards. **/
|
||||
/** Sets the sky's density parameters and calls `recomputeSingleScatter()` 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;
|
||||
if (world.raw.sky_density == null) world.raw.sky_density = new Float32Array(3);
|
||||
var density = world.raw.sky_density;
|
||||
density[0] = Helper.clamp(densityAir, 0, 10);
|
||||
density[1] = Helper.clamp(densityDust, 0, 10);
|
||||
density[2] = Helper.clamp(densityOzone, 0, 10);
|
||||
|
||||
recompute(world);
|
||||
recomputeSingleScatter(world);
|
||||
}
|
||||
|
||||
public static function recomputeMultiScatter(world: WorldData) {
|
||||
if (world == null) return;
|
||||
var raw = world.raw;
|
||||
if (raw.sky_density == null) return;
|
||||
|
||||
if (multiScatterData == null) multiScatterData = new MultipleScatteringData();
|
||||
|
||||
var density = raw.sky_density;
|
||||
var sunElevation = raw.sky_sun_elevation != null ? raw.sky_sun_elevation : 0.0;
|
||||
var sunRotation = raw.sky_sun_rotation != null ? raw.sky_sun_rotation : 0.0;
|
||||
var sunSize = raw.sky_sun_size != null ? raw.sky_sun_size : 0.545;
|
||||
var sunIntensity = raw.sky_sun_intensity != null ? raw.sky_sun_intensity : 1.0;
|
||||
var altitude = raw.sky_altitude != null ? raw.sky_altitude : 0.0;
|
||||
var sunDisc = raw.sky_sun_disc != null ? raw.sky_sun_disc : 1;
|
||||
|
||||
multiScatterData.compute(
|
||||
sunElevation,
|
||||
sunRotation,
|
||||
sunSize,
|
||||
sunIntensity,
|
||||
altitude,
|
||||
sunDisc != 0,
|
||||
new Vec3(density[0], density[1], density[2])
|
||||
);
|
||||
}
|
||||
|
||||
public static function setMultiScatterParams(world: WorldData, sunElevation: FastFloat, sunRotation: FastFloat,
|
||||
sunSize: FastFloat, sunIntensity: FastFloat, altitude: FastFloat, sunDisc: Bool,
|
||||
densityAir: FastFloat, densityDust: FastFloat, densityOzone: FastFloat) {
|
||||
if (world == null) return;
|
||||
|
||||
if (world.raw.sky_density == null) world.raw.sky_density = new Float32Array(3);
|
||||
var density = world.raw.sky_density;
|
||||
density[0] = Helper.clamp(densityAir, 0, 10);
|
||||
density[1] = Helper.clamp(densityDust, 0, 10);
|
||||
density[2] = Helper.clamp(densityOzone, 0, 10);
|
||||
|
||||
world.raw.sky_sun_elevation = sunElevation;
|
||||
world.raw.sky_sun_rotation = sunRotation;
|
||||
world.raw.sky_sun_size = sunSize;
|
||||
world.raw.sky_sun_intensity = sunIntensity;
|
||||
world.raw.sky_altitude = altitude;
|
||||
world.raw.sky_sun_disc = sunDisc ? 1 : 0;
|
||||
|
||||
recomputeMultiScatter(world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This class holds the precalculated result of the inner scattering integral
|
||||
of the Nishita sky model. The outer integral is calculated in
|
||||
of the single scattering sky model. The outer integral is calculated in
|
||||
[`leenkx/Shaders/std/sky.glsl`](https://github.com/leenkx3d/leenkx/blob/master/Shaders/std/sky.glsl).
|
||||
|
||||
@see `leenkx.renderpath.Nishita`
|
||||
@see `leenkx.renderpath.Sky`
|
||||
**/
|
||||
class NishitaData {
|
||||
class SingleScatteringData {
|
||||
|
||||
public var lut: kha.Image;
|
||||
|
||||
@ -229,3 +278,397 @@ class NishitaData {
|
||||
return jAttenuation;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This class precomputes the full sky radiance LUT
|
||||
Unlike the single scattering model matching Blenders implementation.
|
||||
**/
|
||||
|
||||
class MultipleScatteringData {
|
||||
|
||||
public var lut: kha.Image;
|
||||
|
||||
public var sunBottom: Vec3;
|
||||
public var sunTop: Vec3;
|
||||
public var earthIntersectionAngle: FastFloat;
|
||||
|
||||
public static var lutWidth = 256;
|
||||
public static var lutHeight = 128;
|
||||
|
||||
static var transmittanceResX = 256;
|
||||
static var transmittanceResY = 64;
|
||||
var transmittanceLUT: haxe.io.Float32Array;
|
||||
|
||||
static var transmittanceSteps = 64;
|
||||
static var inScatteringSteps = 64;
|
||||
|
||||
// Atmosphere constants sky_multiple_scattering.cpp
|
||||
static var EARTH_RADIUS = 6371.0;
|
||||
static var ATMOSPHERE_THICKNESS = 100.0;
|
||||
static var ATMOSPHERE_RADIUS = 6471.0; // EARTH_RADIUS + ATMOSPHERE_THICKNESS
|
||||
|
||||
static var PHASE_ISOTROPIC = 1.0 / (4.0 * Math.PI);
|
||||
static var RAYLEIGH_PHASE_SCALE = (3.0 / 16.0) * (1.0 / Math.PI);
|
||||
static var G = 0.8;
|
||||
static var SQR_G = 0.64;
|
||||
|
||||
static var GROUND_ALBEDO: Array<Float> = [0.3, 0.3, 0.3, 0.3];
|
||||
|
||||
// Spectral data sampled at 630, 560, 490, 430 nm
|
||||
static var SUN_SPECTRAL_IRRADIANCE: Array<Float> = [1.679, 1.828, 1.986, 1.307];
|
||||
static var MOLECULAR_SCATTERING_COEFFICIENT_BASE: Array<Float> = [6.605e-3, 1.067e-2, 1.842e-2, 3.156e-2];
|
||||
static var OZONE_ABSORPTION_CROSS_SECTION: Array<Float> = [3.472e-25, 3.914e-25, 1.349e-25, 11.03e-27];
|
||||
static var OZONE_MEAN_DOBSON = 334.5;
|
||||
static var AEROSOL_ABSORPTION_CROSS_SECTION: Array<Float> = [2.8722e-24, 4.6168e-24, 7.9706e-24, 1.3578e-23];
|
||||
static var AEROSOL_SCATTERING_CROSS_SECTION: Array<Float> = [1.5908e-22, 1.7711e-22, 2.0942e-22, 2.4033e-22];
|
||||
static var AEROSOL_BASE_DENSITY = 1.3681e20;
|
||||
static var AEROSOL_BACKGROUND_DENSITY = 2e6;
|
||||
static var AEROSOL_HEIGHT_SCALE = 0.73;
|
||||
|
||||
static var SPECTRAL_XYZ: Array<Array<Float>> = [
|
||||
[53.386917738564668023, 22.981337506691024754, 0.0],
|
||||
[43.904844466369358263, 71.347795700053393866, 0.102506867965741307],
|
||||
[1.6137278251608962005, 18.422960591455485011, 31.742921188390805758],
|
||||
[20.762668673810577145, 2.3614213523314368527, 110.48009643252140334]
|
||||
];
|
||||
|
||||
var airDensity: Float = 1.0;
|
||||
var aerosolDensity: Float = 1.0;
|
||||
var ozoneDensity: Float = 1.0;
|
||||
|
||||
public function new() {
|
||||
sunBottom = new Vec3();
|
||||
sunTop = new Vec3();
|
||||
earthIntersectionAngle = 0.0;
|
||||
transmittanceLUT = new haxe.io.Float32Array(transmittanceResY * transmittanceResX * 4);
|
||||
}
|
||||
|
||||
static inline function exp4(a: Array<Float>): Array<Float> {
|
||||
return [Math.exp(a[0]), Math.exp(a[1]), Math.exp(a[2]), Math.exp(a[3])];
|
||||
}
|
||||
|
||||
static inline function scale4(a: Array<Float>, s: Float): Array<Float> {
|
||||
return [a[0] * s, a[1] * s, a[2] * s, a[3] * s];
|
||||
}
|
||||
|
||||
static inline function add4(a: Array<Float>, b: Array<Float>): Array<Float> {
|
||||
return [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3]];
|
||||
}
|
||||
|
||||
static inline function mult4(a: Array<Float>, b: Array<Float>): Array<Float> {
|
||||
return [a[0] * b[0], a[1] * b[1], a[2] * b[2], a[3] * b[3]];
|
||||
}
|
||||
|
||||
static inline function div4(a: Array<Float>, b: Array<Float>): Array<Float> {
|
||||
return [a[0] / b[0], a[1] / b[1], a[2] / b[2], a[3] / b[3]];
|
||||
}
|
||||
|
||||
static inline function max4(a: Array<Float>, b: Float): Array<Float> {
|
||||
return [Math.max(a[0], b), Math.max(a[1], b), Math.max(a[2], b), Math.max(a[3], b)];
|
||||
}
|
||||
|
||||
static inline function mix4(a: Array<Float>, b: Array<Float>, t: Float): Array<Float> {
|
||||
return [a[0] + t * (b[0] - a[0]), a[1] + t * (b[1] - a[1]), a[2] + t * (b[2] - a[2]), a[3] + t * (b[3] - a[3])];
|
||||
}
|
||||
|
||||
// Atmospheric functions
|
||||
function getMolecularScatteringCoefficient(h: Float): Array<Float> {
|
||||
// MOLECULAR_SCATTERING_COEFFICIENT_BASE * exp(-0.07771971 * h^1.16364243)
|
||||
var f = Math.exp(-0.07771971 * Math.pow(h, 1.16364243));
|
||||
return scale4(MOLECULAR_SCATTERING_COEFFICIENT_BASE, f);
|
||||
}
|
||||
|
||||
function getMolecularAbsorptionCoefficient(h: Float): Array<Float> {
|
||||
var logH = Math.log(Math.max(h, 1e-4));
|
||||
var density = 3.78547397e20 * Math.exp(-(logH - 3.22261) * (logH - 3.22261) * 5.55555555 - logH);
|
||||
var result: Array<Float> = [];
|
||||
for (i in 0...4) {
|
||||
result[i] = OZONE_ABSORPTION_CROSS_SECTION[i] * OZONE_MEAN_DOBSON * density;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function getAerosolDensity(h: Float): Float {
|
||||
var division = AEROSOL_BACKGROUND_DENSITY / AEROSOL_BASE_DENSITY;
|
||||
return AEROSOL_BASE_DENSITY * (Math.exp(-h / AEROSOL_HEIGHT_SCALE) + division);
|
||||
}
|
||||
|
||||
function getAtmosphereCollisionCoefficients(h: Float): {
|
||||
aerosolAbs: Array<Float>, aerosolSca: Array<Float>, molAbs: Array<Float>, molSca: Array<Float>
|
||||
} {
|
||||
var localAerosolDensity = getAerosolDensity(h) * aerosolDensity;
|
||||
var aerosolAbs = scale4(AEROSOL_ABSORPTION_CROSS_SECTION, localAerosolDensity);
|
||||
var aerosolSca = scale4(AEROSOL_SCATTERING_CROSS_SECTION, localAerosolDensity);
|
||||
var molAbs = scale4(getMolecularAbsorptionCoefficient(h), ozoneDensity);
|
||||
var molSca = scale4(getMolecularScatteringCoefficient(h), airDensity);
|
||||
return { aerosolAbs: aerosolAbs, aerosolSca: aerosolSca, molAbs: molAbs, molSca: molSca };
|
||||
}
|
||||
|
||||
function molecularPhaseFunction(cosTheta: Float): Float {
|
||||
return RAYLEIGH_PHASE_SCALE * (1.0 + cosTheta * cosTheta);
|
||||
}
|
||||
|
||||
function aerosolPhaseFunction(cosTheta: Float): Float {
|
||||
var den = 1.0 + SQR_G + 2.0 * G * cosTheta;
|
||||
return PHASE_ISOTROPIC * (1.0 - SQR_G) / (den * Math.sqrt(den));
|
||||
}
|
||||
|
||||
|
||||
// nearest positive intersection distance, or -1 if no intersection
|
||||
function raySphereIntersection(pos: Vec3, dir: Vec3, radius: Float): Float {
|
||||
var b = pos.dot(dir);
|
||||
var c = pos.dot(pos) - radius * radius;
|
||||
if (c > 0.0 && b > 0.0) return -1.0;
|
||||
var d = b * b - c;
|
||||
if (d < 0.0) return -1.0;
|
||||
if (d >= b * b) return -b + Math.sqrt(d);
|
||||
return -b - Math.sqrt(d);
|
||||
}
|
||||
|
||||
function sunDirection(cosTheta: Float): Vec3 {
|
||||
// Place sun at azimuth=0 in the atan(dir.x, dir.y) convention
|
||||
// Blender uses (-sqrt(1-cos²θ), 0, cosθ) but our azimuth convention
|
||||
// requires (0, sqrt(1-cos²θ), cosθ) for the sun to be at azimuth=0
|
||||
return new Vec3(0.0, Math.sqrt(1.0 - cosTheta * cosTheta), cosTheta);
|
||||
}
|
||||
|
||||
|
||||
function spectralToXYZ(L: Array<Float>): Vec3 {
|
||||
var x = 0.0, y = 0.0, z = 0.0;
|
||||
for (i in 0...4) {
|
||||
x += SPECTRAL_XYZ[i][0] * L[i];
|
||||
y += SPECTRAL_XYZ[i][1] * L[i];
|
||||
z += SPECTRAL_XYZ[i][2] * L[i];
|
||||
}
|
||||
return new Vec3(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
function getTransmittance(cosTheta: Float, normalizedAltitude: Float): Array<Float> {
|
||||
var sunDir = sunDirection(cosTheta);
|
||||
var distToCenter = EARTH_RADIUS + (ATMOSPHERE_RADIUS - EARTH_RADIUS) * normalizedAltitude;
|
||||
var rayOrigin = new Vec3(0.0, 0.0, distToCenter);
|
||||
var tD = raySphereIntersection(rayOrigin, sunDir, ATMOSPHERE_RADIUS);
|
||||
var tStep = tD / transmittanceSteps;
|
||||
|
||||
var result: Array<Float> = [0.0, 0.0, 0.0, 0.0];
|
||||
for (step in 0...transmittanceSteps) {
|
||||
var t = (step + 0.5) * tStep;
|
||||
var xT = rayOrigin.clone().add(sunDir.clone().mult(t));
|
||||
var altitude = Math.max(xT.length() - EARTH_RADIUS, 0.0);
|
||||
var coeffs = getAtmosphereCollisionCoefficients(altitude);
|
||||
var extinction = add4(add4(coeffs.aerosolAbs, coeffs.aerosolSca), add4(coeffs.molAbs, coeffs.molSca));
|
||||
result = add4(result, scale4(extinction, tStep));
|
||||
}
|
||||
return exp4(scale4(result, -1.0));
|
||||
}
|
||||
|
||||
function precomputeTransmittanceLUT() {
|
||||
for (y in 0...transmittanceResY) {
|
||||
for (x in 0...transmittanceResX) {
|
||||
var u = x / (transmittanceResX - 1);
|
||||
var v = y / (transmittanceResY - 1);
|
||||
var t = getTransmittance(u * 2.0 - 1.0, v);
|
||||
var idx = (y * transmittanceResX + x) * 4;
|
||||
transmittanceLUT[idx] = t[0];
|
||||
transmittanceLUT[idx + 1] = t[1];
|
||||
transmittanceLUT[idx + 2] = t[2];
|
||||
transmittanceLUT[idx + 3] = t[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function lookupTransmittance(cosTheta: Float, normalizedAltitude: Float): Array<Float> {
|
||||
var u = Math.max(0.0, Math.min(1.0, cosTheta * 0.5 + 0.5));
|
||||
var v = Math.max(0.0, Math.min(1.0, normalizedAltitude));
|
||||
var fx = u * (transmittanceResX - 1);
|
||||
var fy = v * (transmittanceResY - 1);
|
||||
var x1 = Std.int(fx);
|
||||
var y1 = Std.int(fy);
|
||||
var x2 = Std.int(Math.min(x1 + 1, transmittanceResX - 1));
|
||||
var y2 = Std.int(Math.min(y1 + 1, transmittanceResY - 1));
|
||||
var dxF = fx - x1;
|
||||
var dyF = fy - y1;
|
||||
|
||||
function getPixel(px: Int, py: Int): Array<Float> {
|
||||
var idx = (py * transmittanceResX + px) * 4;
|
||||
return [transmittanceLUT[idx], transmittanceLUT[idx + 1], transmittanceLUT[idx + 2], transmittanceLUT[idx + 3]];
|
||||
}
|
||||
|
||||
var bottom = mix4(getPixel(x1, y1), getPixel(x2, y1), dxF);
|
||||
var top = mix4(getPixel(x1, y2), getPixel(x2, y2), dxF);
|
||||
return mix4(bottom, top, dyF);
|
||||
}
|
||||
|
||||
function lookupTransmittanceAtGround(cosTheta: Float): Array<Float> {
|
||||
var u = Math.max(0.0, Math.min(1.0, cosTheta * 0.5 + 0.5));
|
||||
var fx = u * (transmittanceResX - 1);
|
||||
var x1 = Std.int(fx);
|
||||
var x2 = Std.int(Math.min(x1 + 1, transmittanceResX - 1));
|
||||
var dxF = fx - x1;
|
||||
var y = 0;
|
||||
function getPixel(px: Int): Array<Float> {
|
||||
var idx = (y * transmittanceResX + px) * 4;
|
||||
return [transmittanceLUT[idx], transmittanceLUT[idx + 1], transmittanceLUT[idx + 2], transmittanceLUT[idx + 3]];
|
||||
}
|
||||
return mix4(getPixel(x1), getPixel(x2), dxF);
|
||||
}
|
||||
|
||||
function lookupTransmittanceToSun(normalizedAltitude: Float): Array<Float> {
|
||||
var v = Math.max(0.0, Math.min(1.0, normalizedAltitude));
|
||||
var fy = v * (transmittanceResY - 1);
|
||||
var y1 = Std.int(fy);
|
||||
var y2 = Std.int(Math.min(y1 + 1, transmittanceResY - 1));
|
||||
var dyF = fy - y1;
|
||||
var x = transmittanceResX - 1;
|
||||
function getPixel(py: Int): Array<Float> {
|
||||
var idx = (py * transmittanceResX + x) * 4;
|
||||
return [transmittanceLUT[idx], transmittanceLUT[idx + 1], transmittanceLUT[idx + 2], transmittanceLUT[idx + 3]];
|
||||
}
|
||||
return mix4(getPixel(y1), getPixel(y2), dyF);
|
||||
}
|
||||
|
||||
// approximation
|
||||
|
||||
function lookupMultiscattering(cosTheta: Float, normalizedHeight: Float, d: Float): Array<Float> {
|
||||
var omega = 2.0 * Math.PI * (1.0 - Math.sqrt(Math.max(0.0, 1.0 - (EARTH_RADIUS / d) * (EARTH_RADIUS / d))));
|
||||
var tToGround = lookupTransmittanceAtGround(cosTheta);
|
||||
var tGroundToSample = div4(lookupTransmittanceToSun(0.0), lookupTransmittanceToSun(normalizedHeight));
|
||||
// 2nd order scattering from the ground
|
||||
var lGround = scale4(mult4(mult4(mult4(scale4(GROUND_ALBEDO, 1.0 / Math.PI), tToGround), tGroundToSample), [cosTheta, cosTheta, cosTheta, cosTheta]), PHASE_ISOTROPIC * omega);
|
||||
// Fit of Earth's multiple scattering from other points in the atmosphere
|
||||
var msFactor = 1.0 / (1.0 + 5.0 * Math.exp(-17.92 * cosTheta));
|
||||
var lMs = scale4([0.217, 0.347, 0.594, 1.0], 0.02 * msFactor);
|
||||
return add4(lMs, lGround);
|
||||
}
|
||||
|
||||
function getInscattering(sunDir: Vec3, rayOrigin: Vec3, rayDir: Vec3, tD: Float): Array<Float> {
|
||||
var cosTheta = rayDir.clone().mult(-1.0).dot(sunDir);
|
||||
var molPhase = molecularPhaseFunction(cosTheta);
|
||||
var aerPhase = aerosolPhaseFunction(cosTheta);
|
||||
var dt = tD / inScatteringSteps;
|
||||
var lInscattering: Array<Float> = [0.0, 0.0, 0.0, 0.0];
|
||||
var transmittance: Array<Float> = [1.0, 1.0, 1.0, 1.0];
|
||||
|
||||
for (i in 0...inScatteringSteps) {
|
||||
var t = (i + 0.5) * dt;
|
||||
var xT = rayOrigin.clone().add(rayDir.clone().mult(t));
|
||||
var distToCenter = xT.length();
|
||||
var zenithDir = xT.clone().mult(1.0 / distToCenter);
|
||||
var altitude = Math.max(distToCenter - EARTH_RADIUS, 0.0);
|
||||
var normalizedAltitude = altitude / ATMOSPHERE_THICKNESS;
|
||||
var sampleCosTheta = zenithDir.dot(sunDir);
|
||||
|
||||
var coeffs = getAtmosphereCollisionCoefficients(altitude);
|
||||
var extinction = add4(add4(coeffs.aerosolAbs, coeffs.aerosolSca), add4(coeffs.molAbs, coeffs.molSca));
|
||||
var tToSun = lookupTransmittance(sampleCosTheta, normalizedAltitude);
|
||||
var ms = lookupMultiscattering(sampleCosTheta, normalizedAltitude, distToCenter);
|
||||
|
||||
// S = SUN_SPECTRAL_IRRADIANCE * (molSca * (molPhase * tToSun + ms) + aerSca * (aerPhase * tToSun + ms))
|
||||
var s: Array<Float> = [0.0, 0.0, 0.0, 0.0];
|
||||
for (w in 0...4) {
|
||||
var molTerm = coeffs.molSca[w] * (molPhase * tToSun[w] + ms[w]);
|
||||
var aerTerm = coeffs.aerosolSca[w] * (aerPhase * tToSun[w] + ms[w]);
|
||||
s[w] = SUN_SPECTRAL_IRRADIANCE[w] * (molTerm + aerTerm);
|
||||
}
|
||||
|
||||
var stepTransmittance = exp4(scale4(extinction, -dt));
|
||||
var cutExt = max4(extinction, 1e-7);
|
||||
var sInt: Array<Float> = [];
|
||||
for (w in 0...4) {
|
||||
sInt[w] = (s[w] - s[w] * stepTransmittance[w]) / cutExt[w];
|
||||
}
|
||||
lInscattering = add4(lInscattering, mult4(transmittance, sInt));
|
||||
transmittance = mult4(transmittance, stepTransmittance);
|
||||
}
|
||||
return lInscattering;
|
||||
}
|
||||
|
||||
|
||||
function computeEarthAngle(altitude: Float): Float {
|
||||
return Math.PI / 2.0 - Math.asin(EARTH_RADIUS / (EARTH_RADIUS + altitude / 1000.0));
|
||||
}
|
||||
|
||||
function precomputeSun(sunElevation: Float, angularDiameter: Float, altitude: Float): { bottom: Vec3, top: Vec3 } {
|
||||
var halfAngular = angularDiameter / 2.0;
|
||||
var solidAngle = 2.0 * Math.PI * (1.0 - Math.cos(halfAngular));
|
||||
var normalizedAltitude = altitude / ATMOSPHERE_THICKNESS;
|
||||
|
||||
function getSunXYZ(elevation: Float): Vec3 {
|
||||
var sunZenithCosAngle = Math.cos(Math.PI / 2.0 - elevation);
|
||||
var tToSun = getTransmittance(sunZenithCosAngle, normalizedAltitude);
|
||||
var spectrum: Array<Float> = [];
|
||||
for (w in 0...4) {
|
||||
spectrum[w] = SUN_SPECTRAL_IRRADIANCE[w] * tToSun[w] / solidAngle;
|
||||
}
|
||||
return spectralToXYZ(spectrum);
|
||||
}
|
||||
|
||||
var bottom = getSunXYZ(sunElevation - halfAngular);
|
||||
var top = getSunXYZ(sunElevation + halfAngular);
|
||||
return { bottom: bottom, top: top };
|
||||
}
|
||||
|
||||
public function compute(sunElevation: Float, sunRotation: Float, sunSize: Float,
|
||||
sunIntensity: Float, altitude: Float, sunDisc: Bool, density: Vec3) {
|
||||
|
||||
airDensity = density.x;
|
||||
aerosolDensity = density.y;
|
||||
ozoneDensity = density.z;
|
||||
|
||||
precomputeTransmittanceLUT();
|
||||
|
||||
var altKm = Math.max(1.0, Math.min(99999.0, altitude)) / 1000.0;
|
||||
var sunZenithCosAngle = Math.cos(Math.PI / 2.0 - sunElevation);
|
||||
var sunDir = sunDirection(sunZenithCosAngle);
|
||||
var rayOrigin = new Vec3(0.0, 0.0, EARTH_RADIUS + altKm);
|
||||
|
||||
earthIntersectionAngle = computeEarthAngle(altitude);
|
||||
|
||||
var imageData = new haxe.io.Float32Array(lutWidth * lutHeight * 4);
|
||||
|
||||
for (y in 0...lutHeight) {
|
||||
var uvY = (y + 0.5) / lutHeight;
|
||||
var l = uvY * 2.0 - 1.0;
|
||||
var elevation = (l < 0 ? -1.0 : 1.0) * l * l * Math.PI / 2.0;
|
||||
var cosEl = Math.cos(elevation);
|
||||
var sinEl = Math.sin(elevation);
|
||||
|
||||
for (x in 0...lutWidth) {
|
||||
var uvX = (x + 0.5) / lutWidth;
|
||||
var azimuth = 2.0 * Math.PI * uvX;
|
||||
|
||||
// atan(dir.x, dir.y) convention
|
||||
var rayDir = new Vec3(
|
||||
Math.sin(azimuth) * cosEl,
|
||||
Math.cos(azimuth) * cosEl,
|
||||
sinEl
|
||||
).normalize();
|
||||
|
||||
var atmosDist = raySphereIntersection(rayOrigin, rayDir, ATMOSPHERE_RADIUS);
|
||||
var groundDist = raySphereIntersection(rayOrigin, rayDir, EARTH_RADIUS);
|
||||
var tD = (groundDist < 0.0) ? atmosDist : groundDist;
|
||||
|
||||
var radiance = getInscattering(sunDir, rayOrigin, rayDir, tD);
|
||||
var xyz = spectralToXYZ(radiance);
|
||||
|
||||
var pixelIndex = (x + y * lutWidth) * 4;
|
||||
imageData[pixelIndex + 0] = xyz.x;
|
||||
imageData[pixelIndex + 1] = xyz.y;
|
||||
imageData[pixelIndex + 2] = xyz.z;
|
||||
imageData[pixelIndex + 3] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
lut = kha.Image.fromBytes(imageData.view.buffer, lutWidth, lutHeight, TextureFormat.RGBA128, Usage.StaticUsage);
|
||||
|
||||
if (sunDisc) {
|
||||
var sunData = precomputeSun(sunElevation, sunSize, altKm);
|
||||
sunBottom = sunData.bottom;
|
||||
sunTop = sunData.top;
|
||||
} else {
|
||||
sunBottom.set(0, 0, 0);
|
||||
sunTop.set(0, 0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -841,18 +841,6 @@ class RenderPathDeferred {
|
||||
}
|
||||
#end
|
||||
|
||||
#if rp_water
|
||||
{
|
||||
path.setTarget("buf");
|
||||
path.bindTarget("tex", "tex");
|
||||
path.drawShader("shader_datas/copy_pass/copy_pass");
|
||||
path.setTarget("tex");
|
||||
path.bindTarget("_main", "gbufferD");
|
||||
path.bindTarget("buf", "tex");
|
||||
path.drawShader("shader_datas/water_pass/water_pass");
|
||||
}
|
||||
#end
|
||||
|
||||
#if (!kha_opengl)
|
||||
path.setDepthFrom("tex", "gbuffer0"); // Re-bind depth
|
||||
#end
|
||||
@ -879,6 +867,18 @@ class RenderPathDeferred {
|
||||
}
|
||||
#end
|
||||
|
||||
#if rp_water
|
||||
{
|
||||
path.setTarget("buf");
|
||||
path.bindTarget("tex", "tex");
|
||||
path.drawShader("shader_datas/copy_pass/copy_pass");
|
||||
path.setTarget("tex");
|
||||
path.bindTarget("_main", "gbufferD");
|
||||
path.bindTarget("buf", "tex");
|
||||
path.drawShader("shader_datas/water_pass/water_pass");
|
||||
}
|
||||
#end
|
||||
|
||||
#if rp_ssr
|
||||
{
|
||||
if (leenkx.data.Config.raw.rp_ssr != false) {
|
||||
|
||||
Reference in New Issue
Block a user