Update Files

This commit is contained in:
2025-01-22 16:18:30 +01:00
parent ed4603cf95
commit a36294b518
16718 changed files with 2960346 additions and 0 deletions

View File

@ -0,0 +1,104 @@
package leenkx.renderpath;
import haxe.ds.ReadOnlyArray;
import iron.RenderPath;
import iron.data.MaterialData;
import iron.object.Object;
import leenkx.math.Helper;
abstract class Downsampler {
public static var currentMipLevel(default, null) = 0;
public static var numMipLevels(default, null) = 0;
static var isRegistered = false;
final path: RenderPath;
final shaderPassHandle: String;
final maxNumMips: Int;
final mipmaps: Array<RenderTarget>;
function new(path: RenderPath, shaderPassHandle: String, maxNumMips: Int) {
this.path = path;
this.shaderPassHandle = shaderPassHandle;
this.maxNumMips = maxNumMips;
this.mipmaps = new Array();
mipmaps.resize(maxNumMips);
}
public static function create(path: RenderPath, shaderPassHandle: String, rtName: String, maxNumMips: Int = 10): Downsampler {
if (!isRegistered) {
isRegistered = true;
iron.object.Uniforms.externalIntLinks.push(intLink);
}
// TODO, implement when Kha supports render targets in compute shaders
// and allows to query whether compute shaders are available
// if (RenderPath.hasComputeSupport()) {
// return new DownsamplerCompute(path, shaderPassHandle, rtName, maxNumMips);
// }
// else {
return new DownsamplerFragment(path, shaderPassHandle, rtName, maxNumMips);
// }
}
static function intLink(object: Object, mat: MaterialData, link: String): Null<Int> {
return switch (link) {
case "_downsampleCurrentMip": Downsampler.currentMipLevel;
case "_downsampleNumMips": Downsampler.numMipLevels;
default: null;
}
}
public inline function getMipmaps(): ReadOnlyArray<RenderTarget> {
return mipmaps;
}
abstract public function dispatch(srcImageName: String, numMips: Int = 0): Void;
}
private class DownsamplerFragment extends Downsampler {
public function new(path: RenderPath, shaderPassHandle: String, rtName: String, maxNumMips: Int) {
super(path, shaderPassHandle, maxNumMips);
var prevScale = 1.0;
for (i in 0...maxNumMips) {
var t = new RenderTargetRaw();
t.name = rtName + "_mip_" + i;
t.width = 0;
t.height = 0;
t.scale = (prevScale *= 0.5);
t.format = Inc.getHdrFormat();
mipmaps[i] = path.createRenderTarget(t);
}
path.loadShader(shaderPassHandle);
}
public function dispatch(srcImageName: String, numMips: Int = 0) {
Helper.clampInt(numMips, 0, maxNumMips);
if (numMips == 0) {
numMips = maxNumMips;
}
final srcImageRT = path.renderTargets.get(srcImageName);
assert(Error, srcImageRT != null, 'Could not find render target with name $srcImageName');
final srcImage = srcImageRT.image;
assert(Error, srcImage != null);
Downsampler.numMipLevels = numMips;
for (i in 0...numMips) {
Downsampler.currentMipLevel = i;
path.setTarget(mipmaps[i].raw.name);
path.clearTarget();
path.bindTarget(i == 0 ? srcImageName : mipmaps[i - 1].raw.name, "tex");
path.drawShader(shaderPassHandle);
}
}
}

View File

@ -0,0 +1,51 @@
package leenkx.renderpath;
import iron.RenderPath;
class DynamicResolutionScale {
public static var dynamicScale = 1.0;
static var firstFrame = true;
static inline var startScaleMs = 30;
static inline var scaleRangeMs = 10;
static inline var maxScale = 0.6;
public static function run(path: RenderPath) {
if (firstFrame) {
iron.App.notifyOnRender(render);
firstFrame = false;
return;
}
// TODO: execute once per frame max
if (frameTimeAvg > startScaleMs && frameTimeAvg < 100) {
var overTime = Math.min(scaleRangeMs, frameTimeAvg - startScaleMs);
var scale = 1.0 - (overTime / scaleRangeMs) * (1.0 - maxScale);
var w = Std.int(iron.App.w() * scale);
var h = Std.int(iron.App.h() * scale);
path.setCurrentViewport(w, h);
path.setCurrentScissor(w, h);
dynamicScale = scale;
}
else dynamicScale = 1.0;
}
static var frameTime: Float;
static var lastTime: Float = 0;
static var totalTime: Float = 0;
static var frames = 0;
static var frameTimeAvg = 0.0;
public static function render(g: kha.graphics4.Graphics) {
frameTime = kha.Scheduler.realTime() - lastTime;
lastTime = kha.Scheduler.realTime();
totalTime += frameTime;
frames++;
if (totalTime >= 1) {
frameTimeAvg = Std.int(totalTime / frames * 10000) / 10;
totalTime = 0;
frames = 0;
}
}
}

View File

@ -0,0 +1,123 @@
// An Analytic Model for Full Spectral Sky-Dome Radiance
// Lukas Hosek and Alexander Wilkie
// Based on https://github.com/ddiakopoulos/sandbox
package leenkx.renderpath;
import kha.math.FastVector3;
import iron.data.WorldData;
class HosekWilkieRadianceData {
public var A = new FastVector3();
public var B = new FastVector3();
public var C = new FastVector3();
public var D = new FastVector3();
public var E = new FastVector3();
public var F = new FastVector3();
public var G = new FastVector3();
public var H = new FastVector3();
public var I = new FastVector3();
public var Z = new FastVector3();
function evaluateSpline(spline: Array<Float>, index: Int, stride: Int, value: Float): Float {
return
1 * Math.pow(1 - value, 5) * spline[index ] +
5 * Math.pow(1 - value, 4) * Math.pow(value, 1) * spline[index + 1 * stride] +
10 * Math.pow(1 - value, 3) * Math.pow(value, 2) * spline[index + 2 * stride] +
10 * Math.pow(1 - value, 2) * Math.pow(value, 3) * spline[index + 3 * stride] +
5 * Math.pow(1 - value, 1) * Math.pow(value, 4) * spline[index + 4 * stride] +
1 * Math.pow(value, 5) * spline[index + 5 * stride];
}
function clamp(n: Int, lower: Int, upper: Int): Int {
return n <= lower ? lower : n >= upper ? upper : n;
}
function clampF(n: Float, lower: Float, upper: Float): Float {
return n <= lower ? lower : n >= upper ? upper : n;
}
function evaluate(dataset: Array<Float>, index: Int, stride: Int, turbidity: Float, albedo: Float, sunTheta: Float): Float {
// Splines are functions of elevation^1/3
var elevationK: Float = Math.pow(Math.max(0.0, 1.0 - sunTheta / (Math.PI / 2.0)), 1.0 / 3.0);
// Table has values for turbidity 1..10
var turbidity0: Int = clamp(Std.int(turbidity), 1, 10);
var turbidity1: Int = Std.int(Math.min(turbidity0 + 1, 10));
var turbidityK: Float = clampF(turbidity - turbidity0, 0.0, 1.0);
var datasetA0Index = index;
var datasetA1Index = index + stride * 6 * 10;
var a0t0: Float = evaluateSpline(dataset, datasetA0Index + stride * 6 * (turbidity0 - 1), stride, elevationK);
var a1t0: Float = evaluateSpline(dataset, datasetA1Index + stride * 6 * (turbidity0 - 1), stride, elevationK);
var a0t1: Float = evaluateSpline(dataset, datasetA0Index + stride * 6 * (turbidity1 - 1), stride, elevationK);
var a1t1: Float = evaluateSpline(dataset, datasetA1Index + stride * 6 * (turbidity1 - 1), stride, elevationK);
return a0t0 * (1 - albedo) * (1 - turbidityK) + a1t0 * albedo * (1 - turbidityK) + a0t1 * (1 - albedo) * turbidityK + a1t1 * albedo * turbidityK;
}
function hosek_wilkie(cos_theta: Float, gamma: Float, cos_gamma: Float, A: FastVector3, B: FastVector3, C: FastVector3, D: FastVector3, E: FastVector3, F: FastVector3, G: FastVector3, H: FastVector3, I: FastVector3): FastVector3 {
var val = (1.0 + cos_gamma * cos_gamma);
var chix = val / Math.pow(1.0 + H.x * H.x - 2.0 * cos_gamma * H.x, 1.5);
var chiy = val / Math.pow(1.0 + H.y * H.y - 2.0 * cos_gamma * H.y, 1.5);
var chiz = val / Math.pow(1.0 + H.z * H.z - 2.0 * cos_gamma * H.z, 1.5);
var chi = new FastVector3(chix, chiy, chiz);
var vx = (1.0 + A.x * Math.exp(B.x / (cos_theta + 0.01))) * (C.x + D.x * Math.exp(E.x * gamma) + F.x * (cos_gamma * cos_gamma) + G.x * chi.x + I.x * Math.sqrt(Math.max(0.0, cos_theta)));
var vy = (1.0 + A.y * Math.exp(B.y / (cos_theta + 0.01))) * (C.y + D.y * Math.exp(E.y * gamma) + F.y * (cos_gamma * cos_gamma) + G.y * chi.y + I.y * Math.sqrt(Math.max(0.0, cos_theta)));
var vz = (1.0 + A.z * Math.exp(B.z / (cos_theta + 0.01))) * (C.z + D.z * Math.exp(E.z * gamma) + F.z * (cos_gamma * cos_gamma) + G.z * chi.z + I.z * Math.sqrt(Math.max(0.0, cos_theta)));
return new FastVector3(vx, vy, vz);
}
function setVector(v: FastVector3, index: Int, f: Float) {
index == 0 ? v.x = f : index == 1 ? v.y = f : v.z = f;
}
public function new() {}
public function recompute(sunTheta: Float, turbidity: kha.FastFloat, albedo: kha.FastFloat, normalizedSunY: Float) {
for (i in 0...3) {
setVector(A, i, evaluate(HosekWilkieData.datasetsRGB[i], 0, 9, turbidity, albedo, sunTheta));
setVector(B, i, evaluate(HosekWilkieData.datasetsRGB[i], 1, 9, turbidity, albedo, sunTheta));
setVector(C, i, evaluate(HosekWilkieData.datasetsRGB[i], 2, 9, turbidity, albedo, sunTheta));
setVector(D, i, evaluate(HosekWilkieData.datasetsRGB[i], 3, 9, turbidity, albedo, sunTheta));
setVector(E, i, evaluate(HosekWilkieData.datasetsRGB[i], 4, 9, turbidity, albedo, sunTheta));
setVector(F, i, evaluate(HosekWilkieData.datasetsRGB[i], 5, 9, turbidity, albedo, sunTheta));
setVector(G, i, evaluate(HosekWilkieData.datasetsRGB[i], 6, 9, turbidity, albedo, sunTheta));
// Swapped in the dataset
setVector(H, i, evaluate(HosekWilkieData.datasetsRGB[i], 8, 9, turbidity, albedo, sunTheta));
setVector(I, i, evaluate(HosekWilkieData.datasetsRGB[i], 7, 9, turbidity, albedo, sunTheta));
setVector(Z, i, evaluate(HosekWilkieData.datasetsRGBRad[i], 0, 1, turbidity, albedo, sunTheta));
}
if (normalizedSunY != 0.0) {
var S: FastVector3 = hosek_wilkie(Math.cos(sunTheta), 0, 1.0, A, B, C, D, E, F, G, H, I);
S.x *= Z.x;
S.y *= Z.y;
S.z *= Z.z;
var dotS = S.dot(new FastVector3(0.2126, 0.7152, 0.0722));
Z.x /= dotS;
Z.y /= dotS;
Z.z /= dotS;
Z = Z.mult(normalizedSunY);
}
}
}
class HosekWilkie {
public static var data: HosekWilkieRadianceData = null;
public static function recompute(world: WorldData) {
if (world == null || world.raw.sun_direction == null) return;
if (data == null) data = new HosekWilkieRadianceData();
// Clamp Z for night cycle
var sunZ = world.raw.sun_direction[2] > 0 ? world.raw.sun_direction[2] : 0;
var sunPositionX = Math.acos(sunZ);
var normalizedSunY: kha.FastFloat = 1.15;
data.recompute(sunPositionX, world.raw.turbidity, world.raw.ground_albedo, normalizedSunY);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,231 @@
package leenkx.renderpath;
import kha.FastFloat;
import kha.arrays.Float32Array;
import kha.graphics4.TextureFormat;
import kha.graphics4.Usage;
import iron.data.WorldData;
import iron.math.Vec2;
import iron.math.Vec3;
import leenkx.math.Helper;
/**
Utility class to control the Nishita sky model.
**/
class Nishita {
public static var data: NishitaData = null;
/**
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.nishita_density == null) return;
if (data == null) data = new NishitaData();
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);
}
}
/**
This class holds the precalculated result of the inner scattering integral
of the Nishita 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`
**/
class NishitaData {
public var lut: kha.Image;
/**
The amount of individual sample heights stored in the LUT (and the width
of the LUT image).
**/
public static var lutHeightSteps = 128;
/**
The amount of individual sun angle steps stored in the LUT (and the
height of the LUT image).
**/
public static var lutAngleSteps = 128;
/**
Amount of steps for calculating the inner scattering integral. Heigher
values are more precise but take longer to compute.
**/
public static var jSteps = 8;
/** Radius of the atmosphere in kilometers. **/
public static var radiusAtmo = 6420.0;
/**
Radius of the planet in kilometers. The default value is the earth radius as
defined in Cycles.
**/
public static var radiusPlanet = 6360.0;
/** Rayleigh scattering coefficient. **/
public static var rayleighCoeff = new Vec3(5.5e-6, 13.0e-6, 22.4e-6);
/** Rayleigh scattering scale parameter. **/
public static var rayleighScale = 8e3;
/** Mie scattering coefficient. **/
public static var mieCoeff = 2e-5;
/** Mie scattering scale parameter. **/
public static var mieScale = 1.2e3;
/** Ozone scattering coefficient. **/
// The ozone absorption coefficients are taken from Cycles code.
// Because Cycles calculates 21 wavelengths, we use the coefficients
// which are closest to the RGB wavelengths (645nm, 510nm, 440nm).
// Precalculating values by simulating Blender's spec_to_xyz() function
// to include all 21 wavelengths gave unrealistic results.
public static var ozoneCoeff = new Vec3(1.59051840791988e-6, 0.00000096707041180970, 0.00000007309568762914);
public function new() {}
/** Approximates the density of ozone for a given sample height. **/
function getOzoneDensity(height: FastFloat): FastFloat {
// Values are taken from Cycles code
if (height < 10000.0 || height >= 40000.0) {
return 0.0;
}
if (height < 25000.0) {
return (height - 10000.0) / 15000.0;
}
return -((height - 40000.0) / 15000.0);
}
/**
Ray-sphere intersection test that assumes the sphere is centered at the
origin. There is no intersection when result.x > result.y. Otherwise
this function returns the distances to the two intersection points,
which might be equal.
**/
function raySphereIntersection(rayOrigin: Vec3, rayDirection: Vec3, sphereRadius: Float): Vec2 {
// Algorithm is described here: https://en.wikipedia.org/wiki/Line%E2%80%93sphere_intersection
var a = rayDirection.dot(rayDirection);
var b = 2.0 * rayDirection.dot(rayOrigin);
var c = rayOrigin.dot(rayOrigin) - (sphereRadius * sphereRadius);
var d = (b * b) - 4.0 * a * c;
// Ray does not intersect the sphere
if (d < 0.0) return new Vec2(1e5, -1e5);
return new Vec2(
(-b - Math.sqrt(d)) / (2.0 * a),
(-b + Math.sqrt(d)) / (2.0 * a)
);
}
/**
Computes the LUT texture for the given density values.
@param density 3D vector of air density, dust density, ozone density
**/
public function computeLUT(density: Vec3) {
var imageData = new haxe.io.Float32Array(lutHeightSteps * lutAngleSteps * 4);
for (x in 0...lutHeightSteps) {
var height = (x / (lutHeightSteps - 1));
// Use quadratic height for better horizon precision
height *= height;
height *= radiusAtmo * 1000; // Denormalize height
for (y in 0...lutAngleSteps) {
var sunTheta = y / (lutAngleSteps - 1) * 2 - 1;
// Improve horizon precision
// See https://sebh.github.io/publications/egsr2020.pdf (5.3)
sunTheta = Helper.sign(sunTheta) * sunTheta * sunTheta;
sunTheta = sunTheta * Math.PI / 2 + Math.PI / 2; // Denormalize
var jODepth = sampleSecondaryRay(height, sunTheta, density);
var pixelIndex = (x + y * lutHeightSteps) * 4;
imageData[pixelIndex + 0] = jODepth.x;
imageData[pixelIndex + 1] = jODepth.y;
imageData[pixelIndex + 2] = jODepth.z;
imageData[pixelIndex + 3] = 1.0; // Unused
}
}
lut = kha.Image.fromBytes(imageData.view.buffer, lutHeightSteps, lutAngleSteps, TextureFormat.RGBA128, Usage.StaticUsage);
}
/**
Calculates the integral for the secondary ray.
**/
public function sampleSecondaryRay(height: FastFloat, sunTheta: FastFloat, density: Vec3): Vec3 {
var radiusPlanetMeters = radiusPlanet * 1000;
// Reconstruct values from the shader
var iPos = new Vec3(0, 0, height + radiusPlanetMeters);
var pSun = new Vec3(0.0, Math.sin(sunTheta), Math.cos(sunTheta)).normalize();
var jTime: FastFloat = 0.0;
// We compute the ray-sphere intersection in km to allow larger
// atmosphere radii (radius is squared inside raySphereIntersection())
var jStepSize: FastFloat = raySphereIntersection(iPos.clone().mult(0.001), pSun, radiusAtmo).y / jSteps;
jStepSize *= 1000; // convert back to m
// Optical depth accumulators for the secondary ray (Rayleigh, Mie, ozone)
var jODepth = new Vec3();
for (i in 0...jSteps) {
// Calculate the secondary ray sample position and height
var jPos = iPos.clone().add(pSun.clone().mult(jTime + jStepSize * 0.5));
var jHeight = jPos.length() - radiusPlanetMeters;
// Accumulate optical depth
var optDepthRayleigh = Math.exp(-jHeight / rayleighScale) * density.x;
var optDepthMie = Math.exp(-jHeight / mieScale) * density.y;
var optDepthOzone = getOzoneDensity(jHeight) * density.z;
jODepth.addf(optDepthRayleigh, optDepthMie, optDepthOzone);
jTime += jStepSize;
}
jODepth.mult(jStepSize);
// Precalculate a part of the secondary attenuation.
// For one variable (e.g. x) in the vector, the formula is as follows:
//
// attn.x = exp(-(coeffX * (firstOpticalDepth.x + secondOpticalDepth.x)))
//
// We can split that up via:
//
// attn.x = exp(-(coeffX * firstOpticalDepth.x + coeffX * secondOpticalDepth.x))
// = exp(-(coeffX * firstOpticalDepth.x)) * exp(-(coeffX * secondOpticalDepth.x))
//
// The first factor of the resulting multiplication is calculated in the
// shader, but we can already precalculate the second one. As a side
// effect this keeps the range of the LUT values small because we don't
// store the optical depth but the attenuation.
var jAttenuation = new Vec3();
var mie = mieCoeff * jODepth.y;
jAttenuation.addf(mie, mie, mie);
jAttenuation.add(rayleighCoeff.clone().mult(jODepth.x));
jAttenuation.add(ozoneCoeff.clone().mult(jODepth.z));
jAttenuation.exp(jAttenuation.mult(-1));
return jAttenuation;
}
}

View File

@ -0,0 +1,351 @@
package leenkx.renderpath;
import iron.data.MaterialData;
import iron.math.Vec4;
import iron.object.Object;
class Postprocess {
public static var colorgrading_global_uniforms = [
[6500.0, 1.0, 0.0], //0: Whitebalance, Shadow Max, Highlight Min
[1.0, 1.0, 1.0], //1: Tint
[1.0, 1.0, 1.0], //2: Saturation
[1.0, 1.0, 1.0], //3: Contrast
[1.0, 1.0, 1.0], //4: Gamma
[1.0, 1.0, 1.0], //5: Gain
[1.0, 1.0, 1.0], //6: Offset
[1.0, 1.0, 1.0] //7: LUT Strength
];
public static var colorgrading_shadow_uniforms = [
[1.0, 1.0, 1.0], //0: Saturation
[1.0, 1.0, 1.0], //1: Contrast
[1.0, 1.0, 1.0], //2: Gamma
[1.0, 1.0, 1.0], //3: Gain
[1.0, 1.0, 1.0] //4: Offset
];
public static var colorgrading_midtone_uniforms = [
[1.0, 1.0, 1.0], //0: Saturation
[1.0, 1.0, 1.0], //1: Contrast
[1.0, 1.0, 1.0], //2: Gamma
[1.0, 1.0, 1.0], //3: Gain
[1.0, 1.0, 1.0] //4: Offset
];
public static var colorgrading_highlight_uniforms = [
[1.0, 1.0, 1.0], //0: Saturation
[1.0, 1.0, 1.0], //1: Contrast
[1.0, 1.0, 1.0], //2: Gamma
[1.0, 1.0, 1.0], //3: Gain
[1.0, 1.0, 1.0] //4: Offset
];
public static var camera_uniforms = [
1.0, //0: Camera: F-Number
2.8333, //1: Camera: Shutter time
100.0, //2: Camera: ISO
0.0, //3: Camera: Exposure Compensation
0.01, //4: Fisheye Distortion
1, //5: DoF AutoFocus §§ If true, it ignores the DoF Distance setting
10.0, //6: DoF Distance
160.0, //7: DoF Focal Length mm
128, //8: DoF F-Stop
0, //9: Tonemapping Method
2.0, //10: Distort
2.0, //11: Film Grain
0.25, //12: Sharpen
0.7 //13: Vignette
];
public static var tonemapper_uniforms = [
1.0, //0: Slope
1.0, //1: Toe
1.0, //2: Shoulder
1.0, //3: Black Clip
1.0 //4: White Clip
];
public static var letterbox_uniforms = [
[0.0, 0.0, 0.0], //0: Colors
[0.1] //1: Size
];
public static var ssr_uniforms = [
0.04, //0: Step
0.05, //1: StepMin
5.0, //2: Search
5.0, //3: Falloff
0.6 //4: Jitter
];
public static var bloom_uniforms = [
0.8, // 0: Threshold
0.5, // 1: Knee
0.05, // 2: Strength
0.0, // 3: Sample scale (value set by renderpath, not used for realtime postprocess)
];
public static var ssao_uniforms = [
1.0,
1.0,
8
];
public static var lenstexture_uniforms = [
0.1, //0: Center Min Clip
0.5, //1: Center Max Clip
0.1, //2: Luminance Min
2.5, //3: Luminance Max
2.0 //4: Brightness Exponent
];
public static var chromatic_aberration_uniforms = [
2.0, //0: Strength
32 //1: Samples
];
public static function vec3Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
var v:Vec4 = null;
switch link {
case "_globalWeight":
var ppm_index = 0;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalTint":
var ppm_index = 1;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalSaturation":
var ppm_index = 2;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalContrast":
var ppm_index = 3;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalGamma":
var ppm_index = 4;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalGain":
var ppm_index = 5;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
case "_globalOffset":
var ppm_index = 6;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_global_uniforms[ppm_index][0];
v.y = colorgrading_global_uniforms[ppm_index][1];
v.z = colorgrading_global_uniforms[ppm_index][2];
//Shadow ppm
case "_shadowSaturation":
var ppm_index = 0;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_shadow_uniforms[ppm_index][0];
v.y = colorgrading_shadow_uniforms[ppm_index][1];
v.z = colorgrading_shadow_uniforms[ppm_index][2];
case "_shadowContrast":
var ppm_index = 1;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_shadow_uniforms[ppm_index][0];
v.y = colorgrading_shadow_uniforms[ppm_index][1];
v.z = colorgrading_shadow_uniforms[ppm_index][2];
case "_shadowGamma":
var ppm_index = 2;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_shadow_uniforms[ppm_index][0];
v.y = colorgrading_shadow_uniforms[ppm_index][1];
v.z = colorgrading_shadow_uniforms[ppm_index][2];
case "_shadowGain":
var ppm_index = 3;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_shadow_uniforms[ppm_index][0];
v.y = colorgrading_shadow_uniforms[ppm_index][1];
v.z = colorgrading_shadow_uniforms[ppm_index][2];
case "_shadowOffset":
var ppm_index = 4;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_shadow_uniforms[ppm_index][0];
v.y = colorgrading_shadow_uniforms[ppm_index][1];
v.z = colorgrading_shadow_uniforms[ppm_index][2];
//Midtone ppm
case "_midtoneSaturation":
var ppm_index = 0;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_midtone_uniforms[ppm_index][0];
v.y = colorgrading_midtone_uniforms[ppm_index][1];
v.z = colorgrading_midtone_uniforms[ppm_index][2];
case "_midtoneContrast":
var ppm_index = 1;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_midtone_uniforms[ppm_index][0];
v.y = colorgrading_midtone_uniforms[ppm_index][1];
v.z = colorgrading_midtone_uniforms[ppm_index][2];
case "_midtoneGamma":
var ppm_index = 2;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_midtone_uniforms[ppm_index][0];
v.y = colorgrading_midtone_uniforms[ppm_index][1];
v.z = colorgrading_midtone_uniforms[ppm_index][2];
case "_midtoneGain":
var ppm_index = 3;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_midtone_uniforms[ppm_index][0];
v.y = colorgrading_midtone_uniforms[ppm_index][1];
v.z = colorgrading_midtone_uniforms[ppm_index][2];
case "_midtoneOffset":
var ppm_index = 4;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_midtone_uniforms[ppm_index][0];
v.y = colorgrading_midtone_uniforms[ppm_index][1];
v.z = colorgrading_midtone_uniforms[ppm_index][2];
//Highlight ppm
case "_highlightSaturation":
var ppm_index = 0;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_highlight_uniforms[ppm_index][0];
v.y = colorgrading_highlight_uniforms[ppm_index][1];
v.z = colorgrading_highlight_uniforms[ppm_index][2];
case "_highlightContrast":
var ppm_index = 1;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_highlight_uniforms[ppm_index][0];
v.y = colorgrading_highlight_uniforms[ppm_index][1];
v.z = colorgrading_highlight_uniforms[ppm_index][2];
case "_highlightGamma":
var ppm_index = 2;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_highlight_uniforms[ppm_index][0];
v.y = colorgrading_highlight_uniforms[ppm_index][1];
v.z = colorgrading_highlight_uniforms[ppm_index][2];
case "_highlightGain":
var ppm_index = 3;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_highlight_uniforms[ppm_index][0];
v.y = colorgrading_highlight_uniforms[ppm_index][1];
v.z = colorgrading_highlight_uniforms[ppm_index][2];
case "_highlightOffset":
var ppm_index = 4;
v = iron.object.Uniforms.helpVec;
v.x = colorgrading_highlight_uniforms[ppm_index][0];
v.y = colorgrading_highlight_uniforms[ppm_index][1];
v.z = colorgrading_highlight_uniforms[ppm_index][2];
//Postprocess Components
case "_PPComp1":
v = iron.object.Uniforms.helpVec;
v.x = camera_uniforms[0]; //F-Number
v.y = camera_uniforms[1]; //Shutter
v.z = camera_uniforms[2]; //ISO
case "_PPComp2":
v = iron.object.Uniforms.helpVec;
v.x = camera_uniforms[3]; //EC
v.y = camera_uniforms[4]; //Lens Distortion
v.z = camera_uniforms[5]; //DOF Autofocus
case "_PPComp3":
v = iron.object.Uniforms.helpVec;
v.x = camera_uniforms[6]; //Distance
v.y = camera_uniforms[7]; //Focal Length
v.z = camera_uniforms[8]; //F-Stop
case "_PPComp4":
v = iron.object.Uniforms.helpVec;
v.x = Std.int(camera_uniforms[9]); //Tonemapping
v.y = camera_uniforms[11]; //Film Grain
v.z = tonemapper_uniforms[0]; //Slope
case "_PPComp5":
v = iron.object.Uniforms.helpVec;
v.x = tonemapper_uniforms[1]; //Toe
v.y = tonemapper_uniforms[2]; //Shoulder
v.z = tonemapper_uniforms[3]; //Black Clip
case "_PPComp6":
v = iron.object.Uniforms.helpVec;
v.x = tonemapper_uniforms[4]; //White Clip
v.y = lenstexture_uniforms[0]; //Center Min
v.z = lenstexture_uniforms[1]; //Center Max
case "_PPComp7":
v = iron.object.Uniforms.helpVec;
v.x = lenstexture_uniforms[2]; //Lum min
v.y = lenstexture_uniforms[3]; //Lum max
v.z = lenstexture_uniforms[4]; //Expo
case "_PPComp9":
v = iron.object.Uniforms.helpVec;
v.x = ssr_uniforms[0]; //Step
v.y = ssr_uniforms[1]; //StepMin
v.z = ssr_uniforms[2]; //Search
case "_PPComp10":
v = iron.object.Uniforms.helpVec;
v.x = ssr_uniforms[3]; //Falloff
v.y = ssr_uniforms[4]; //Jitter
v.z = 0;
case "_PPComp11":
v = iron.object.Uniforms.helpVec;
v.x = bloom_uniforms[2]; // Bloom Strength
v.y = 0; // Unused
v.z = 0; // Unused
case "_PPComp12":
v = iron.object.Uniforms.helpVec;
v.x = ssao_uniforms[0]; //SSAO Strength
v.y = ssao_uniforms[1]; //SSAO Radius
v.z = ssao_uniforms[2]; //SSAO Max Steps
case "_PPComp13":
v = iron.object.Uniforms.helpVec;
v.x = chromatic_aberration_uniforms[0]; //CA Strength
v.y = chromatic_aberration_uniforms[1]; //CA Samples
v.z = 0;
case "_PPComp14":
v = iron.object.Uniforms.helpVec;
v.x = camera_uniforms[10]; //Distort
v.y = camera_uniforms[12]; //Sharpen
v.z = camera_uniforms[13]; //Vignette
}
return v;
}
public static function vec4Link(object: Object, mat: MaterialData, link: String): iron.math.Vec4 {
var v: Vec4 = null;
switch link {
case "_BloomThresholdData":
if (Downsampler.currentMipLevel == 0) {
// See https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/#3.4
v = iron.object.Uniforms.helpVec;
v.x = bloom_uniforms[0];
v.y = v.x - bloom_uniforms[1];
v.z = 2 * bloom_uniforms[1];
v.w = 0.25 / (bloom_uniforms[1] + 6.2e-5);
}
case "_PPComp15":
v = iron.object.Uniforms.helpVec;
v.x = letterbox_uniforms[0][0]; //Color
v.y = letterbox_uniforms[0][1];
v.z = letterbox_uniforms[0][2];
v.w = letterbox_uniforms[1][0]; //Size
}
return v;
}
public static function init() {
iron.object.Uniforms.externalVec3Links.push(vec3Link);
iron.object.Uniforms.externalVec4Links.push(vec4Link);
}
}

View File

@ -0,0 +1,62 @@
// Reference: https://github.com/leenkx3d/leenkx_docs/blob/master/dev/renderpath.md
package leenkx.renderpath;
import iron.RenderPath;
class RenderPathCreator {
public static var path: RenderPath;
public static var commands: Void->Void = function() {};
#if (rp_renderer == "Forward")
public static var setTargetMeshes: Void->Void = RenderPathForward.setTargetMeshes;
public static var drawMeshes: Void->Void = RenderPathForward.drawMeshes;
public static var applyConfig: Void->Void = RenderPathForward.applyConfig;
#elseif (rp_renderer == "Deferred")
public static var setTargetMeshes: Void->Void = RenderPathDeferred.setTargetMeshes;
public static var drawMeshes: Void->Void = RenderPathDeferred.drawMeshes;
public static var applyConfig: Void->Void = RenderPathDeferred.applyConfig;
#else
public static var setTargetMeshes: Void->Void = function() {};
public static var drawMeshes: Void->Void = function() {};
public static var applyConfig: Void->Void = function() {};
#end
public static function get(): RenderPath {
path = new RenderPath();
Inc.init(path);
#if rp_pp
iron.App.notifyOnInit(function() {
Postprocess.init();
});
#end
#if (rp_renderer == "Forward")
RenderPathForward.init(path);
path.commands = function() {
RenderPathForward.commands();
commands();
}
path.setupDepthTexture = RenderPathForward.setupDepthTexture;
#elseif (rp_renderer == "Deferred")
RenderPathDeferred.init(path);
path.commands = function() {
RenderPathDeferred.commands();
commands();
}
path.setupDepthTexture = RenderPathDeferred.setupDepthTexture;
#elseif (rp_renderer == "Raytracer")
RenderPathRaytracer.init(path);
path.commands = function() {
RenderPathRaytracer.commands();
commands();
}
#end
return path;
}
// Last target before drawing to framebuffer
public static var finalTarget: RenderTarget = null;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,736 @@
package leenkx.renderpath;
import iron.RenderPath;
import iron.Scene;
import iron.object.Clipmap;
class RenderPathForward {
#if (rp_renderer == "Forward")
static var path: RenderPath;
#if (rp_voxels != "Off")
static var res_pre_clear = true;
#end
#if rp_bloom
static var bloomDownsampler: Downsampler;
static var bloomUpsampler: Upsampler;
#end
public static function setTargetMeshes() {
#if rp_render_to_texture
{
path.setTarget("lbuffer0", [
#if (rp_ssr || rp_ssrefr) "lbuffer1", #end
#if (rp_ssrefr || lnx_voxelgi_refract) "gbuffer_refraction" #end]
);
}
#else
{
path.setTarget("");
}
#end
}
public static function drawMeshes() {
path.drawMeshes("mesh");
#if (rp_background == "World")
{
if (Scene.active.raw.world_ref != null) {
RenderPathCreator.setTargetMeshes();
path.drawSkydome("shader_datas/World_" + Scene.active.raw.world_ref + "/World_" + Scene.active.raw.world_ref);
}
}
#end
#if rp_blending
{
RenderPathCreator.setTargetMeshes();
path.drawMeshes("blend");
}
#end
#if (rp_translucency && !rp_ssrefr)
{
RenderPathCreator.setTargetMeshes();
Inc.drawTranslucency("lbuffer0");
}
#end
}
public static function applyConfig() {
Inc.applyConfig();
}
public static function init(_path: RenderPath) {
path = _path;
#if kha_metal
{
path.loadShader("shader_datas/clear_color_depth_pass/clear_color_depth_pass");
path.loadShader("shader_datas/clear_color_pass/clear_color_pass");
path.loadShader("shader_datas/clear_depth_pass/clear_depth_pass");
path.clearShader = "shader_datas/clear_color_depth_pass/clear_color_depth_pass";
}
#end
#if rp_depth_texture
{
var t = new RenderTargetRaw();
t.name = "depthtex";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if (rp_render_to_texture)
{
path.createDepthBuffer("main", "DEPTH24");
var t = new RenderTargetRaw();
t.name = "lbuffer0";
t.width = 0;
t.height = 0;
t.format = Inc.getHdrFormat();
t.displayp = Inc.getDisplayp();
t.scale = Inc.getSuperSampling();
t.depth_buffer = "main";
path.createRenderTarget(t);
#if (rp_ssr || rp_ssrefr)
{
var t = new RenderTargetRaw();
t.name = "lbuffer1";
t.width = 0;
t.height = 0;
t.format = Inc.getHdrFormat();
t.displayp = Inc.getDisplayp();
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if (rp_ssrefr || lnx_voxelgi_refract)
{
var t = new RenderTargetRaw();
t.name = "gbuffer_refraction";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA64";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if rp_ssrefr
{
path.loadShader("shader_datas/ssrefr_pass/ssrefr_pass");
path.loadShader("shader_datas/copy_pass/copy_pass");
// holds background depth
var t = new RenderTargetRaw();
t.name = "gbufferD1";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "refr";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA64";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if rp_compositornodes
{
path.loadShader("shader_datas/compositor_pass/compositor_pass");
}
#else
{
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
#if ((rp_supersampling == 4) || (rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || (rp_depth_texture))
{
var t = new RenderTargetRaw();
t.name = "buf";
t.width = 0;
t.height = 0;
t.format = "RGBA32";
t.displayp = Inc.getDisplayp();
t.scale = Inc.getSuperSampling();
t.depth_buffer = "main";
path.createRenderTarget(t);
}
#end
#if (rp_supersampling == 4)
{
path.loadShader("shader_datas/supersample_resolve/supersample_resolve");
}
#end
}
#end
#if (rp_translucency && !rp_ssrefr)
{
Inc.initTranslucency();
}
#end
#if (rp_voxels != "Off")
{
Inc.initGI("voxels");
Inc.initGI("voxelsOut");
Inc.initGI("voxelsOutB");
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
Inc.initGI("voxelsSDF");
Inc.initGI("voxelsSDFtmp");
#end
#if (rp_voxels == "Voxel GI")
Inc.initGI("voxelsLight");
Inc.initGI("voxels_diffuse");
Inc.initGI("voxels_specular");
#else
Inc.initGI("voxels_ao");
#end
iron.RenderPath.clipmaps = new Array<Clipmap>();
for (i in 0...Main.voxelgiClipmapCount) {
var clipmap = new iron.object.Clipmap();
clipmap.voxelSize = Main.voxelgiVoxelSize * Math.pow(2.0, i);
clipmap.extents = new iron.math.Vec3(0.0);
clipmap.center = new iron.math.Vec3(0.0);
clipmap.offset_prev = new iron.math.Vec3(0.0);
iron.RenderPath.clipmaps.push(clipmap);
}
}
#end
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA") || (rp_ssr && !rp_ssr_half) || (rp_water) || (rp_depth_texture))
{
var t = new RenderTargetRaw();
t.name = "bufa";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "bufb";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "RGBA32";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
path.loadShader("shader_datas/smaa_edge_detect/smaa_edge_detect");
path.loadShader("shader_datas/smaa_blend_weight/smaa_blend_weight");
path.loadShader("shader_datas/smaa_neighborhood_blend/smaa_neighborhood_blend");
#if (rp_antialiasing == "TAA")
{
path.loadShader("shader_datas/taa_pass/taa_pass");
}
#end
#end
#if rp_volumetriclight
{
path.loadShader("shader_datas/volumetric_light/volumetric_light");
path.loadShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.loadShader("shader_datas/blur_bilat_blend_pass/blur_bilat_blend_pass_y");
var t = new RenderTargetRaw();
t.name = "singlea";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "singleb";
t.width = 0;
t.height = 0;
t.displayp = Inc.getDisplayp();
t.format = "R8";
t.scale = Inc.getSuperSampling();
path.createRenderTarget(t);
}
#end
#if rp_water
{
path.loadShader("shader_datas/water_pass/water_pass");
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
#if rp_depth_texture
{
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
#if rp_bloom
{
bloomDownsampler = Downsampler.create(path, "shader_datas/bloom_pass/downsample_pass", "bloom");
bloomUpsampler = Upsampler.create(path, "shader_datas/bloom_pass/upsample_pass", bloomDownsampler.getMipmaps());
}
#end
#if (rp_ssr_half || rp_ssgi_half || (rp_voxels != "Off"))
{
path.loadShader("shader_datas/downsample_depth/downsample_depth");
var t = new RenderTargetRaw();
t.name = "half";
t.width = 0;
t.height = 0;
t.scale = Inc.getSuperSampling() * 0.5;
t.format = "R32"; // R16
path.createRenderTarget(t);
}
#end
#if rp_ssr
{
path.loadShader("shader_datas/ssr_pass/ssr_pass");
path.loadShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x");
path.loadShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend");
#if rp_ssr_half
{
var t = new RenderTargetRaw();
t.name = "ssra";
t.width = 0;
t.height = 0;
t.scale = Inc.getSuperSampling() * 0.5;
t.format = Inc.getHdrFormat();
path.createRenderTarget(t);
var t = new RenderTargetRaw();
t.name = "ssrb";
t.width = 0;
t.height = 0;
t.scale = Inc.getSuperSampling() * 0.5;
t.format = Inc.getHdrFormat();
path.createRenderTarget(t);
}
#end
}
#end
#if rp_chromatic_aberration
{
path.loadShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.loadShader("shader_datas/copy_pass/copy_pass");
}
#end
}
public static function commands() {
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.drawShadowMapAtlas();
#else
Inc.drawShadowMap();
#end
}
#end
// Voxels
#if (rp_voxels != 'Off')
if (leenkx.data.Config.raw.rp_gi != false)
{
var path = RenderPath.active;
Inc.computeVoxelsBegin();
if (iron.RenderPath.pre_clear == true)
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
path.clearImage("voxelsOut", 0x00000000);
path.clearImage("voxelsOutB", 0x00000000);
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
path.clearImage("voxelsSDF", 0x00000000);
path.clearImage("voxelsSDFtmp", 0x00000000);
#end
iron.RenderPath.pre_clear = false;
}
else
{
#if (rp_voxels == "Voxel GI")
path.clearImage("voxelsLight", 0x00000000);
#end
path.clearImage("voxels", 0x00000000);
Inc.computeVoxelsOffsetPrev();
}
path.setTarget("");
var res = iron.RenderPath.getVoxelRes();
path.setViewport(res, res);
path.bindTarget("voxels", "voxels");
path.drawMeshes("voxel");
#if (rp_voxels == "Voxel GI")
Inc.computeVoxelsLight();
#end
Inc.computeVoxelsTemporal();
#if (lnx_voxelgi_shadows || (rp_voxels == "Voxel GI"))
Inc.computeVoxelsSDF();
#end
if (iron.RenderPath.res_pre_clear == true)
{
iron.RenderPath.res_pre_clear = false;
#if (rp_voxels == "Voxel GI")
path.clearImage("voxels_diffuse", 0x00000000);
path.clearImage("voxels_specular", 0x00000000);
#else
path.clearImage("voxels_ao", 0x00000000);
#end
}
}
#end
RenderPathCreator.setTargetMeshes();
#if (rp_background == "Clear")
{
path.clearTarget(-1, 1.0);
}
#else
{
path.clearTarget(null, 1.0);
}
#end
#if (rp_ssrefr || lnx_voxelgi_refract)
{
path.setTarget("gbuffer_refraction"); // Only clear gbuffer0
path.clearTarget(0xff000000);
}
#end
#if rp_depthprepass
{
path.drawMeshes("depth");
}
#end
#if rp_ssrefr
{
path.setTarget("gbuffer_refraction");
path.clearTarget(0xffffff00);
}
#end
RenderPathCreator.setTargetMeshes();
#if rp_shadowmap
{
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
}
#end
#if (rp_voxels != "Off")
if (leenkx.data.Config.raw.rp_gi != false)
{
#if (rp_voxels == "Voxel AO")
Inc.resolveAO();
path.bindTarget("voxels_ao", "voxels_ao");
#else
Inc.resolveDiffuse();
Inc.resolveSpecular();
path.bindTarget("voxels_diffuse", "voxels_diffuse");
path.bindTarget("voxels_specular", "voxels_specular");
#end
#if lnx_voxelgi_shadows
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
}
#end
#if rp_stereo
{
path.drawStereo(drawMeshes);
}
#else
{
RenderPathCreator.drawMeshes();
}
#end
#if (rp_render_to_texture || rp_voxels != "Off")
{
#if (rp_ssr_half || rp_ssgi_half || rp_voxels != "Off")
path.setTarget("half");
path.bindTarget("_main", "texdepth");
path.drawShader("shader_datas/downsample_depth/downsample_depth");
#end
#if rp_ssrefr
{
if (leenkx.data.Config.raw.rp_ssrefr != false)
{
//save depth
path.setTarget("gbufferD1");
path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
//save background color
path.setTarget("refr");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("lbuffer0", ["lbuffer1", "gbuffer_refraction"]);
#if (rp_voxels != "Off")
path.bindTarget("voxelsOut", "voxels");
path.bindTarget("voxelsSDF", "voxelsSDF");
#end
path.drawMeshes("refraction");
path.setTarget("lbuffer0");
path.bindTarget("lbuffer0", "tex");
path.bindTarget("refr", "tex1");
path.bindTarget("_main", "gbufferD");
path.bindTarget("gbufferD1", "gbufferD1");
path.bindTarget("lbuffer1", "gbuffer0");
path.bindTarget("gbuffer_refraction", "gbuffer_refraction");
path.drawShader("shader_datas/ssrefr_pass/ssrefr_pass");
}
}
#end
#if rp_ssr
{
if (leenkx.data.Config.raw.rp_ssr != false) {
#if rp_ssr_half
var targeta = "ssra";
var targetb = "ssrb";
#else
var targeta = "bufa";
var targetb = "bufb";
#end
path.setTarget(targeta);
path.bindTarget("lbuffer0", "tex");
#if rp_ssr_half
path.bindTarget("half", "gbufferD");
#else
path.bindTarget("_main", "gbufferD");
#end
path.bindTarget("lbuffer1", "gbuffer0");
path.bindTarget("lbuffer0", "gbuffer1");
path.drawShader("shader_datas/ssr_pass/ssr_pass");
path.setTarget(targetb);
path.bindTarget(targeta, "tex");
path.bindTarget("lbuffer1", "gbuffer0");
path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_x");
path.setTarget("lbuffer0");
path.bindTarget(targetb, "tex");
path.bindTarget("lbuffer1", "gbuffer0");
path.drawShader("shader_datas/blur_adaptive_pass/blur_adaptive_pass_y3_blend");
}
}
#end
#if rp_bloom
{
inline Inc.drawBloom("lbuffer0", bloomDownsampler, bloomUpsampler);
}
#end
#if rp_volumetriclight
{
path.setTarget("singlea");
path.bindTarget("_main", "gbufferD");
#if lnx_shadowmap_atlas
Inc.bindShadowMapAtlas();
#else
Inc.bindShadowMap();
#end
path.drawShader("shader_datas/volumetric_light/volumetric_light");
path.setTarget("singleb");
path.bindTarget("singlea", "tex");
path.drawShader("shader_datas/blur_bilat_pass/blur_bilat_pass_x");
path.setTarget("lbuffer0");
path.bindTarget("singleb", "tex");
path.drawShader("shader_datas/blur_bilat_blend_pass/blur_bilat_blend_pass_y");
}
#end
#if rp_water
{
path.setDepthFrom("lbuffer0", "bufa"); // Unbind depth so we can read it
path.depthToRenderTarget.set("main", path.renderTargets.get("buf"));
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
path.setTarget("lbuffer0");
path.bindTarget("_main", "gbufferD");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/water_pass/water_pass");
path.setDepthFrom("lbuffer0", "buf"); // Re-bind depth
path.depthToRenderTarget.set("main", path.renderTargets.get("lbuffer0"));
}
#end
#if rp_chromatic_aberration
{
path.setTarget("bufa");
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/chromatic_aberration_pass/chromatic_aberration_pass");
path.setTarget("lbuffer0");
path.bindTarget("bufa", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
#if (rp_supersampling == 4)
var framebuffer = "buf";
#else
var framebuffer = "";
#end
#if ((rp_antialiasing == "Off") || (rp_antialiasing == "FXAA"))
{
RenderPathCreator.finalTarget = path.currentTarget;
path.setTarget(framebuffer);
}
#else
{
path.setTarget("buf");
RenderPathCreator.finalTarget = path.currentTarget;
}
#end
#if rp_compositordepth
{
path.bindTarget("_main", "gbufferD");
}
#end
#if rp_compositornodes
{
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/compositor_pass/compositor_pass");
}
#else
{
path.bindTarget("lbuffer0", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
}
#end
#if ((rp_antialiasing == "SMAA") || (rp_antialiasing == "TAA"))
{
path.setTarget("bufa");
path.clearTarget(0x00000000);
path.bindTarget("buf", "colorTex");
path.drawShader("shader_datas/smaa_edge_detect/smaa_edge_detect");
path.setTarget("bufb");
path.clearTarget(0x00000000);
path.bindTarget("bufa", "edgesTex");
path.drawShader("shader_datas/smaa_blend_weight/smaa_blend_weight");
path.setTarget(framebuffer);
path.bindTarget("buf", "colorTex");
path.bindTarget("bufb", "blendTex");
path.drawShader("shader_datas/smaa_neighborhood_blend/smaa_neighborhood_blend");
}
#end
#if (rp_supersampling == 4)
{
var finalTarget = "";
path.setTarget(finalTarget);
path.bindTarget(framebuffer, "tex");
path.drawShader("shader_datas/supersample_resolve/supersample_resolve");
}
#end
}
#end
#if rp_overlays
{
path.clearTarget(null, 1.0);
path.drawMeshes("overlay");
}
#end
}
public static function setupDepthTexture() {
// When render to texture is off, lbuffer0 does not exist, so for
// now do nothing then and pass an empty uniform to the shader
#if rp_render_to_texture
#if (!kha_opengl)
path.setDepthFrom("lbuffer0", "bufa"); // Unbind depth so we can read it
path.depthToRenderTarget.set("main", path.renderTargets.get("buf"));
#end
// Copy the depth buffer to the depth texture
path.setTarget("depthtex");
path.bindTarget("_main", "tex");
path.drawShader("shader_datas/copy_pass/copy_pass");
#if (!kha_opengl)
path.setDepthFrom("lbuffer0", "buf"); // Re-bind depth
path.depthToRenderTarget.set("main", path.renderTargets.get("lbuffer0"));
#end
#end // rp_render_to_texture
setTargetMeshes();
path.bindTarget("depthtex", "depthtex");
}
#end
}

View File

@ -0,0 +1,150 @@
package leenkx.renderpath;
#if kha_dxr
import kha.graphics5.CommandList;
import kha.graphics5.ConstantBuffer;
import kha.graphics5.RayTraceTarget;
import kha.graphics5.RayTracePipeline;
import kha.graphics5.AccelerationStructure;
import kha.graphics5.VertexBuffer;
import kha.graphics5.IndexBuffer;
import kha.graphics5.VertexStructure;
import kha.graphics5.VertexData;
import kha.graphics5.TextureFormat;
import iron.RenderPath;
class RenderPathRaytracer {
#if (rp_renderer == "Raytracer")
static var path: RenderPath;
static var ready = false;
static inline var bufferCount = 2;
static var currentBuffer = -1;
static var commandList: CommandList;
static var framebuffers = new haxe.ds.Vector<kha.graphics5.RenderTarget>(bufferCount);
static var constantBuffer: ConstantBuffer;
static var target: RayTraceTarget;
static var pipeline: RayTracePipeline;
static var accel: AccelerationStructure;
static var frame = 0.0;
@:access(iron.data.Geometry)
public static function init(_path: RenderPath) {
path = _path;
kha.Assets.loadBlobFromPath("raytrace.cso", function(rayTraceShader: kha.Blob) {
ready = true;
// Command list
commandList = new CommandList();
for (i in 0...bufferCount) {
framebuffers[i] = new kha.graphics5.RenderTarget(iron.App.w(), iron.App.h(), 16, false, TextureFormat.RGBA32,
-1, -i - 1 /* hack in an index for backbuffer render targets */);
}
commandList.end(); // TODO: Otherwise "Reset fails because the command list was not closed"
// Pipeline
constantBuffer = new ConstantBuffer(21 * 4);
pipeline = new RayTracePipeline(commandList, rayTraceShader, constantBuffer);
// Acceleration structure
var structure = new VertexStructure();
structure.add("pos", VertexData.Float3);
structure.add("nor", VertexData.Float3);
structure.add("tex", VertexData.Float2);
var md = iron.Scene.active.meshes[0].data;
var geom = md.geom;
var verts = Std.int(geom.positions.length / 4);
var vb = new VertexBuffer(verts, structure, kha.graphics5.Usage.StaticUsage);
var vba = vb.lock();
// iron.data.Geometry.buildVertices(vba, geom.positions, geom.normals);
for (i in 0...verts) {
vba[i * 8 ] = (geom.positions[i * 4 ] / 32767) * md.scalePos;
vba[i * 8 + 1] = (geom.positions[i * 4 + 1] / 32767) * md.scalePos;
vba[i * 8 + 2] = (geom.positions[i * 4 + 2] / 32767) * md.scalePos;
vba[i * 8 + 3] = geom.normals [i * 2 ] / 32767;
vba[i * 8 + 4] = geom.normals [i * 2 + 1] / 32767;
vba[i * 8 + 5] = geom.positions[i * 4 + 3] / 32767;
vba[i * 8 + 6] = (geom.uvs[i * 2 ] / 32767) * md.scaleTex;
vba[i * 8 + 7] = (geom.uvs[i * 2 + 1] / 32767) * md.scaleTex;
}
vb.unlock();
var id = geom.indices[0];
var ib = new IndexBuffer(id.length, kha.graphics5.Usage.StaticUsage);
var iba = ib.lock();
for (i in 0...iba.length) iba[i] = id[i];
ib.unlock();
accel = new AccelerationStructure(commandList, vb, ib);
// Output
target = new RayTraceTarget(iron.App.w(), iron.App.h());
});
}
public static function commands() {
if (!ready) return;
var g = iron.App.framebuffer.g5;
currentBuffer = (currentBuffer + 1) % bufferCount;
constantBuffer.lock();
var cam = iron.Scene.active.camera;
var ct = cam.transform;
var helpMat = iron.math.Mat4.identity();
helpMat.setFrom(cam.V);
helpMat.multmat(cam.P);
helpMat.getInverse(helpMat);
constantBuffer.setFloat(0, ct.worldx());
constantBuffer.setFloat(4, ct.worldy());
constantBuffer.setFloat(8, ct.worldz());
constantBuffer.setFloat(12, 1);
constantBuffer.setFloat(16, helpMat._00);
constantBuffer.setFloat(20, helpMat._01);
constantBuffer.setFloat(24, helpMat._02);
constantBuffer.setFloat(28, helpMat._03);
constantBuffer.setFloat(32, helpMat._10);
constantBuffer.setFloat(36, helpMat._11);
constantBuffer.setFloat(40, helpMat._12);
constantBuffer.setFloat(44, helpMat._13);
constantBuffer.setFloat(48, helpMat._20);
constantBuffer.setFloat(52, helpMat._21);
constantBuffer.setFloat(56, helpMat._22);
constantBuffer.setFloat(60, helpMat._23);
constantBuffer.setFloat(64, helpMat._30);
constantBuffer.setFloat(68, helpMat._31);
constantBuffer.setFloat(72, helpMat._32);
constantBuffer.setFloat(76, helpMat._33);
constantBuffer.setFloat(80, frame);
frame += 1.0;
constantBuffer.unlock();
g.begin(framebuffers[currentBuffer]);
commandList.begin();
g.setAccelerationStructure(accel);
g.setRayTracePipeline(pipeline);
g.setRayTraceTarget(target);
g.dispatchRays(commandList);
g.copyRayTraceTarget(commandList, framebuffers[currentBuffer], target);
commandList.end();
g.end();
// g.swapBuffers();
if (iron.system.Input.getMouse().down()) frame = 1.0;
}
#end
}
#end

View File

@ -0,0 +1,21 @@
package leenkx.renderpath;
class RenderToTexture{
/**
The current kha g2 object to be rendered to.
**/
public static var g: Null<kha.graphics2.Graphics> = null;
public static inline function ensureEmptyRenderTarget(location: String){
assert(Error, g == null,
'render texture already exists at $location. Please clear the texture before setting.
If used in logic node, please consult its documentation.'
);
}
public static inline function ensure2DContext(location: String) {
assert(Error, g != null,
'$location must be executed inside of a render2D callback. If used in logic node, please consult its documentation.'
);
}
}

View File

@ -0,0 +1,83 @@
package leenkx.renderpath;
import haxe.ds.ReadOnlyArray;
import iron.RenderPath;
import iron.data.MaterialData;
import iron.object.Object;
import leenkx.math.Helper;
abstract class Upsampler {
public static var currentMipLevel(default, null) = 0;
public static var numMipLevels(default, null) = 0;
static var isRegistered = false;
final path: RenderPath;
final shaderPassHandle: String;
final mipmaps: ReadOnlyArray<RenderTarget>;
function new(path: RenderPath, shaderPassHandle: String, mipmaps: ReadOnlyArray<RenderTarget>) {
this.path = path;
this.shaderPassHandle = shaderPassHandle;
this.mipmaps = mipmaps;
}
public static function create(path: RenderPath, shaderPassHandle: String, mipmaps: ReadOnlyArray<RenderTarget>): Upsampler {
if (!isRegistered) {
isRegistered = true;
iron.object.Uniforms.externalIntLinks.push(intLink);
}
// TODO, see Downsampler.hx
// if (RenderPath.hasComputeSupport()) {
// return new UpsamplerCompute(path, shaderPassHandle, mipmaps);
// }
// else {
return new UpsamplerFragment(path, shaderPassHandle, mipmaps);
// }
}
static function intLink(object: Object, mat: MaterialData, link: String): Null<Int> {
return switch (link) {
case "_upsampleCurrentMip": Upsampler.currentMipLevel;
case "_upsampleNumMips": Upsampler.numMipLevels;
default: null;
}
}
abstract public function dispatch(dstImageName: String, numMips: Int = 0): Void;
}
private class UpsamplerFragment extends Upsampler {
public function new(path: RenderPath, shaderPassHandle: String, mipmaps: ReadOnlyArray<RenderTarget>) {
super(path, shaderPassHandle, mipmaps);
path.loadShader(shaderPassHandle);
}
public function dispatch(dstImageName: String, numMips: Int = 0) {
Helper.clampInt(numMips, 0, mipmaps.length);
if (numMips == 0) {
numMips = mipmaps.length;
}
final srcImageRT = path.renderTargets.get(dstImageName);
assert(Error, srcImageRT != null);
final srcImage = srcImageRT.image;
assert(Error, srcImage != null);
Upsampler.numMipLevels = numMips;
for (i in 0...Upsampler.numMipLevels) {
final mipLevel = Upsampler.numMipLevels - 1 - i;
Upsampler.currentMipLevel = mipLevel;
path.setTarget(mipLevel == 0 ? dstImageName : mipmaps[mipLevel - 1].raw.name);
path.bindTarget(mipmaps[mipLevel].raw.name, "tex");
path.drawShader(shaderPassHandle);
}
}
}