Compare commits
131 Commits
2b221338ae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c537591e8 | |||
| ee04bb87dd | |||
| 612a3d0a09 | |||
| 3273054766 | |||
| beabf76562 | |||
| c1aa2837ca | |||
| c320c4bb19 | |||
| 4c8c26bb33 | |||
| a133c5ee94 | |||
| 4895d7cfa5 | |||
| 052ce82588 | |||
| 211cba61a6 | |||
| 4ab646015d | |||
| 9fee67d231 | |||
| 860b73a8f4 | |||
| da38631584 | |||
| 6839203dc5 | |||
| d9ac74101f | |||
| e20400654c | |||
| 058cbed20f | |||
| e0c10318e5 | |||
| 7d609b126d | |||
| 36ecaea919 | |||
| fac01a2849 | |||
| c71c1a98e4 | |||
| f6d5f142e9 | |||
| 70961b0336 | |||
| 9c50274f29 | |||
| a94b95afc8 | |||
| cee36635e0 | |||
| d0337f5dae | |||
| fca51c7e61 | |||
| 0bb4bceb57 | |||
| af9a259fbb | |||
| f1d8522f51 | |||
| 56d1b0e4a7 | |||
| 73e4a0ad30 | |||
| 67d5e6b2c7 | |||
| 635f8f6645 | |||
| b2444be7e2 | |||
| 7d00036973 | |||
| 46814444af | |||
| b4de377433 | |||
| 2abb6efe4f | |||
| d787f9a7fc | |||
| 75e95387bb | |||
| 0067459e9a | |||
| cf68067343 | |||
| 365c624d73 | |||
| 384a8a7ee8 | |||
| 4e2b59a1d6 | |||
| 6d07e70b11 | |||
| 42fdff4757 | |||
| d44fd9993a | |||
| 3fd1b395fb | |||
| 40bca62f96 | |||
| e5082835c4 | |||
| bc700a6374 | |||
| 1e500993d9 | |||
| d842744aeb | |||
| ada18b4648 | |||
| 5004efe046 | |||
| 1015e0df34 | |||
| 949b355255 | |||
| 0460b9d587 | |||
| 88cebe4f49 | |||
| c915312901 | |||
| 0f9d693bb7 | |||
| ce09e510e9 | |||
| 7319040624 | |||
| 542773bd79 | |||
| fe017dd874 | |||
| 7aeebf2008 | |||
| 0b4184ccc2 | |||
| ddc12e8607 | |||
| 6ad647ae56 | |||
| b77aca926a | |||
| c2bb20f905 | |||
| 14c6a7be03 | |||
| 85d63e8413 | |||
| 78452aaf67 | |||
| 1f72636350 | |||
| 62433ce86a | |||
| 2be36398f7 | |||
| 2675138ddc | |||
| 572665e8e6 | |||
| 0839f39dfa | |||
| 57cf4955a1 | |||
| c52ae2e4f1 | |||
| 48141f23c5 | |||
| cbbcd053fb | |||
| a57923860e | |||
| 160f6bdadf | |||
| 50ad462318 | |||
| 1239145da4 | |||
| 102941b5d2 | |||
| defff993a0 | |||
| e408151295 | |||
| 22159c9f37 | |||
| 9a2779982d | |||
| 2fc7e9880a | |||
| 7e1ac5fa5c | |||
| b2395f30fc | |||
| f4b0bf1e93 | |||
| 31a6a9d7ec | |||
| 7b2f21c499 | |||
| 17c31b4a4b | |||
| 013c8653ff | |||
| bd8b49a416 | |||
| 9d83c318b6 | |||
| cb19c9b5b4 | |||
| f4fe822ef6 | |||
| 6689fbc734 | |||
| 91b6c203e9 | |||
| 2c30554504 | |||
| 37c8779d12 | |||
| 6b704ff469 | |||
| 2a3bff5a18 | |||
| 91482071b8 | |||
| 38151eb233 | |||
| 78ea055aea | |||
| db2482dbe2 | |||
| 5ae6a9e698 | |||
| 6db83e559b | |||
| e7ec872747 | |||
| 31226a3871 | |||
| 9be3240d6c | |||
| 96134404a2 | |||
| 3aac63d255 | |||
| a8d6095204 | |||
| 960095d0d5 |
@ -142,6 +142,8 @@ class Image implements Canvas implements Resource {
|
||||
return 5;
|
||||
case A16:
|
||||
return 7;
|
||||
case R32UI:
|
||||
return 8;
|
||||
default:
|
||||
return 1; // Grey8
|
||||
}
|
||||
|
||||
@ -1,397 +1,397 @@
|
||||
package kha.korehl.graphics4;
|
||||
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.graphics4.ComputeShader;
|
||||
import kha.graphics4.CubeMap;
|
||||
import kha.graphics4.MipMapFilter;
|
||||
import kha.graphics4.PipelineState;
|
||||
import kha.graphics4.ShaderStorageBuffer;
|
||||
import kha.graphics4.TextureAddressing;
|
||||
import kha.graphics4.TextureFilter;
|
||||
import kha.graphics4.Usage;
|
||||
import kha.graphics4.VertexBuffer;
|
||||
import kha.math.FastMatrix3;
|
||||
import kha.math.FastMatrix4;
|
||||
import kha.math.FastVector2;
|
||||
import kha.math.FastVector3;
|
||||
import kha.math.FastVector4;
|
||||
import kha.Canvas;
|
||||
import kha.Image;
|
||||
import kha.Video;
|
||||
import kha.Color;
|
||||
|
||||
class Graphics implements kha.graphics4.Graphics {
|
||||
var target: Canvas;
|
||||
|
||||
public function new(target: Canvas = null) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public function vsynced(): Bool {
|
||||
return kinc_graphics_vsynced();
|
||||
}
|
||||
|
||||
public function refreshRate(): Int {
|
||||
return kinc_graphics_refreshrate();
|
||||
}
|
||||
|
||||
public function clear(?color: Color, ?z: FastFloat, ?stencil: Int): Void {
|
||||
var flags: Int = 0;
|
||||
if (color != null)
|
||||
flags |= 1;
|
||||
if (z != null)
|
||||
flags |= 2;
|
||||
if (stencil != null)
|
||||
flags |= 4;
|
||||
kinc_graphics_clear(flags, color == null ? 0 : color.value, z, stencil);
|
||||
}
|
||||
|
||||
public function viewport(x: Int, y: Int, width: Int, height: Int): Void {
|
||||
kinc_graphics_viewport(x, y, width, height);
|
||||
}
|
||||
|
||||
public function setVertexBuffer(vertexBuffer: kha.graphics4.VertexBuffer): Void {
|
||||
kinc_graphics_set_vertexbuffer(vertexBuffer._buffer);
|
||||
}
|
||||
|
||||
public function setVertexBuffers(vertexBuffers: Array<kha.graphics4.VertexBuffer>): Void {
|
||||
kinc_graphics_set_vertexbuffers(vertexBuffers.length > 0 ? vertexBuffers[0]._buffer : null,
|
||||
vertexBuffers.length > 1 ? vertexBuffers[1]._buffer : null, vertexBuffers.length > 2 ? vertexBuffers[2]._buffer : null,
|
||||
vertexBuffers.length > 3 ? vertexBuffers[3]._buffer : null, vertexBuffers.length);
|
||||
}
|
||||
|
||||
public function setIndexBuffer(indexBuffer: kha.graphics4.IndexBuffer): Void {
|
||||
kinc_graphics_set_indexbuffer(indexBuffer._buffer);
|
||||
}
|
||||
|
||||
public function maxTextureSize(): Int {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
public function supportsNonPow2Textures(): Bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setCubeMap(unit: kha.graphics4.TextureUnit, cubeMap: kha.graphics4.CubeMap): Void {
|
||||
if (cubeMap == null)
|
||||
return;
|
||||
if (cubeMap._texture != null)
|
||||
kinc_graphics_set_cubemap_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._texture);
|
||||
else
|
||||
kinc_graphics_set_cubemap_target(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._renderTarget);
|
||||
}
|
||||
|
||||
public function setCubeMapDepth(unit: kha.graphics4.TextureUnit, cubeMap: kha.graphics4.CubeMap): Void {
|
||||
if (cubeMap == null)
|
||||
return;
|
||||
kinc_graphics_set_cubemap_depth(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._renderTarget);
|
||||
}
|
||||
|
||||
public function scissor(x: Int, y: Int, width: Int, height: Int): Void {
|
||||
kinc_graphics_scissor(x, y, width, height);
|
||||
}
|
||||
|
||||
public function disableScissor(): Void {
|
||||
kinc_graphics_disable_scissor();
|
||||
}
|
||||
|
||||
public function instancedRenderingAvailable(): Bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setTextureParameters(unit: kha.graphics4.TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void {
|
||||
kinc_graphics_set_texture_parameters(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, uAddressing, vAddressing, minificationFilter,
|
||||
magnificationFilter, mipmapFilter);
|
||||
}
|
||||
|
||||
public function setTexture3DParameters(unit: kha.graphics4.TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
wAddressing: TextureAddressing, minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void {
|
||||
kinc_graphics_set_texture3d_parameters(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, uAddressing, vAddressing, wAddressing, minificationFilter,
|
||||
magnificationFilter, mipmapFilter);
|
||||
}
|
||||
|
||||
public function setTextureCompareMode(unit: kha.graphics4.TextureUnit, enabled: Bool) {
|
||||
kinc_graphics_set_texture_compare_mode(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, enabled);
|
||||
}
|
||||
|
||||
public function setCubeMapCompareMode(unit: kha.graphics4.TextureUnit, enabled: Bool) {
|
||||
kinc_graphics_set_cube_map_compare_mode(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, enabled);
|
||||
}
|
||||
|
||||
public function setTexture(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
if (texture._texture != null)
|
||||
kinc_graphics_set_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._texture);
|
||||
else
|
||||
kinc_graphics_set_render_target(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._renderTarget);
|
||||
}
|
||||
|
||||
public function setTextureArray(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture_array(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._textureArray);
|
||||
}
|
||||
|
||||
public function setTextureDepth(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture_depth(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._renderTarget);
|
||||
}
|
||||
|
||||
public function setVideoTexture(unit: kha.graphics4.TextureUnit, texture: kha.Video): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, Image.fromVideo(texture)._texture);
|
||||
}
|
||||
|
||||
public function setImageTexture(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
kinc_graphics_set_image_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._texture);
|
||||
}
|
||||
|
||||
public function maxBoundTextures(): Int {
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function setPipeline(pipe: PipelineState): Void {
|
||||
pipe.set();
|
||||
}
|
||||
|
||||
public function setStencilReferenceValue(value: Int): Void {}
|
||||
|
||||
public function setBool(location: kha.graphics4.ConstantLocation, value: Bool): Void {
|
||||
kinc_graphics_set_bool(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setInt(location: kha.graphics4.ConstantLocation, value: Int): Void {
|
||||
kinc_graphics_set_int(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setInt2(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int): Void {
|
||||
kinc_graphics_set_int2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2);
|
||||
}
|
||||
|
||||
public function setInt3(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int, value3: Int): Void {
|
||||
kinc_graphics_set_int3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3);
|
||||
}
|
||||
|
||||
public function setInt4(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int, value3: Int, value4: Int): Void {
|
||||
kinc_graphics_set_int4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3, value4);
|
||||
}
|
||||
|
||||
public function setInts(location: kha.graphics4.ConstantLocation, values: kha.arrays.Int32Array): Void {
|
||||
kinc_graphics_set_ints(cast(location, kha.korehl.graphics4.ConstantLocation)._location, values.getData(), values.length);
|
||||
}
|
||||
|
||||
public function setFloat(location: kha.graphics4.ConstantLocation, value: FastFloat): Void {
|
||||
kinc_graphics_set_float(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setFloat2(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat): Void {
|
||||
kinc_graphics_set_float2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2);
|
||||
}
|
||||
|
||||
public function setFloat3(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat): Void {
|
||||
kinc_graphics_set_float3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3);
|
||||
}
|
||||
|
||||
public function setFloat4(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat, value4: FastFloat): Void {
|
||||
kinc_graphics_set_float4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3, value4);
|
||||
}
|
||||
|
||||
public function setVector2(location: kha.graphics4.ConstantLocation, value: FastVector2): Void {
|
||||
kinc_graphics_set_float2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y);
|
||||
}
|
||||
|
||||
public function setVector3(location: kha.graphics4.ConstantLocation, value: FastVector3): Void {
|
||||
kinc_graphics_set_float3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
public function setVector4(location: kha.graphics4.ConstantLocation, value: FastVector4): Void {
|
||||
kinc_graphics_set_float4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y, value.z, value.w);
|
||||
}
|
||||
|
||||
public function setFloats(location: kha.graphics4.ConstantLocation, values: Float32Array): Void {
|
||||
kinc_graphics_set_floats(cast(location, kha.korehl.graphics4.ConstantLocation)._location, values.getData(), values.length);
|
||||
}
|
||||
|
||||
public inline function setMatrix(location: kha.graphics4.ConstantLocation, matrix: FastMatrix4): Void {
|
||||
kinc_graphics_set_matrix(cast(location, kha.korehl.graphics4.ConstantLocation)._location, matrix._00, matrix._10, matrix._20, matrix._30, matrix._01,
|
||||
matrix._11, matrix._21, matrix._31, matrix._02, matrix._12, matrix._22, matrix._32, matrix._03, matrix._13, matrix._23, matrix._33);
|
||||
}
|
||||
|
||||
public inline function setMatrix3(location: kha.graphics4.ConstantLocation, matrix: FastMatrix3): Void {
|
||||
kinc_graphics_set_matrix3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, matrix._00, matrix._10, matrix._20, matrix._01, matrix._11,
|
||||
matrix._21, matrix._02, matrix._12, matrix._22);
|
||||
}
|
||||
|
||||
public function drawIndexedVertices(start: Int = 0, count: Int = -1): Void {
|
||||
if (count < 0)
|
||||
kinc_graphics_draw_all_indexed_vertices();
|
||||
else
|
||||
kinc_graphics_draw_indexed_vertices(start, count);
|
||||
}
|
||||
|
||||
public function drawIndexedVerticesInstanced(instanceCount: Int, start: Int = 0, count: Int = -1): Void {
|
||||
if (count < 0)
|
||||
kinc_graphics_draw_all_indexed_vertices_instanced(instanceCount);
|
||||
else
|
||||
kinc_graphics_draw_indexed_vertices_instanced(instanceCount, start, count);
|
||||
}
|
||||
|
||||
function renderToTexture(additionalRenderTargets: Array<Canvas>): Void {
|
||||
if (additionalRenderTargets != null) {
|
||||
var len = additionalRenderTargets.length;
|
||||
var rt0 = cast(target, Image)._renderTarget;
|
||||
var rt1 = len > 0 ? cast(additionalRenderTargets[0], Image)._renderTarget : null;
|
||||
var rt2 = len > 1 ? cast(additionalRenderTargets[1], Image)._renderTarget : null;
|
||||
var rt3 = len > 2 ? cast(additionalRenderTargets[2], Image)._renderTarget : null;
|
||||
var rt4 = len > 3 ? cast(additionalRenderTargets[3], Image)._renderTarget : null;
|
||||
var rt5 = len > 4 ? cast(additionalRenderTargets[4], Image)._renderTarget : null;
|
||||
var rt6 = len > 5 ? cast(additionalRenderTargets[5], Image)._renderTarget : null;
|
||||
var rt7 = len > 6 ? cast(additionalRenderTargets[6], Image)._renderTarget : null;
|
||||
kinc_graphics_render_to_textures(rt0, rt1, rt2, rt3, rt4, rt5, rt6, rt7, len + 1);
|
||||
}
|
||||
else {
|
||||
kinc_graphics_render_to_texture(cast(target, Image)._renderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public function begin(additionalRenderTargets: Array<Canvas> = null): Void {
|
||||
if (target == null)
|
||||
kinc_graphics_restore_render_target();
|
||||
else
|
||||
renderToTexture(additionalRenderTargets);
|
||||
}
|
||||
|
||||
public function beginFace(face: Int): Void {
|
||||
kinc_graphics_render_to_face(cast(target, CubeMap)._renderTarget, face);
|
||||
}
|
||||
|
||||
public function beginEye(eye: Int): Void {}
|
||||
|
||||
public function end(): Void {}
|
||||
|
||||
public function flush(): Void {
|
||||
kinc_graphics_flush();
|
||||
}
|
||||
|
||||
public function setShaderStorageBuffer(buffer: ShaderStorageBuffer, index: Int) {
|
||||
// Kore::Compute::setBuffer(buffer->buffer, index);
|
||||
}
|
||||
|
||||
public function setComputeShader(shader: ComputeShader) {
|
||||
kinc_g4_set_compute_shader(shader._shader);
|
||||
}
|
||||
|
||||
public function compute(x: Int, y: Int, z: Int) {
|
||||
kinc_g4_compute(x, y, z);
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_clear") static function kinc_graphics_clear(flags: Int, color: Int, z: FastFloat, stencil: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_vsynced") static function kinc_graphics_vsynced(): Bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_refreshrate") static function kinc_graphics_refreshrate(): Int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_viewport") static function kinc_graphics_viewport(x: Int, y: Int, width: Int, height: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_vertexbuffer") static function kinc_graphics_set_vertexbuffer(buffer: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_vertexbuffers") static function kinc_graphics_set_vertexbuffers(b0: Pointer, b1: Pointer, b2: Pointer, b3: Pointer,
|
||||
count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_indexbuffer") static function kinc_graphics_set_indexbuffer(buffer: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_scissor") static function kinc_graphics_scissor(x: Int, y: Int, width: Int, height: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_disable_scissor") static function kinc_graphics_disable_scissor(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_parameters") static function kinc_graphics_set_texture_parameters(unit: Pointer, uAddressing: Int,
|
||||
vAddressing: Int, minificationFilter: Int, magnificationFilter: Int, mipmapFilter: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture3d_parameters") static function kinc_graphics_set_texture3d_parameters(unit: Pointer, uAddressing: Int,
|
||||
vAddressing: Int, wAddressing: Int, minificationFilter: Int, magnificationFilter: Int, mipmapFilter: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_compare_mode") static function kinc_graphics_set_texture_compare_mode(unit: Pointer, enabled: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cube_map_compare_mode") static function kinc_graphics_set_cube_map_compare_mode(unit: Pointer, enabled: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture") static function kinc_graphics_set_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_depth") static function kinc_graphics_set_texture_depth(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_array") static function kinc_graphics_set_texture_array(unit: Pointer, textureArray: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_render_target") static function kinc_graphics_set_render_target(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_texture") static function kinc_graphics_set_cubemap_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_target") static function kinc_graphics_set_cubemap_target(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_depth") static function kinc_graphics_set_cubemap_depth(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_image_texture") static function kinc_graphics_set_image_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_bool") static function kinc_graphics_set_bool(location: Pointer, value: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int") static function kinc_graphics_set_int(location: Pointer, value: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int2") static function kinc_graphics_set_int2(location: Pointer, value1: Int, value2: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int3") static function kinc_graphics_set_int3(location: Pointer, value1: Int, value2: Int, value3: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int4") static function kinc_graphics_set_int4(location: Pointer, value1: Int, value2: Int, value3: Int,
|
||||
value4: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_ints") static function kinc_graphics_set_ints(location: Pointer, values: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float") static function kinc_graphics_set_float(location: Pointer, value: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float2") static function kinc_graphics_set_float2(location: Pointer, value1: FastFloat, value2: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float3") static function kinc_graphics_set_float3(location: Pointer, value1: FastFloat, value2: FastFloat,
|
||||
value3: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float4") static function kinc_graphics_set_float4(location: Pointer, value1: FastFloat, value2: FastFloat,
|
||||
value3: FastFloat, value4: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_floats") static function kinc_graphics_set_floats(location: Pointer, values: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_matrix") static function kinc_graphics_set_matrix(location: Pointer, _00: FastFloat, _10: FastFloat, _20: FastFloat,
|
||||
_30: FastFloat, _01: FastFloat, _11: FastFloat, _21: FastFloat, _31: FastFloat, _02: FastFloat, _12: FastFloat, _22: FastFloat, _32: FastFloat,
|
||||
_03: FastFloat, _13: FastFloat, _23: FastFloat, _33: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_matrix3") static function kinc_graphics_set_matrix3(location: Pointer, _00: FastFloat, _10: FastFloat,
|
||||
_20: FastFloat, _01: FastFloat, _11: FastFloat, _21: FastFloat, _02: FastFloat, _12: FastFloat, _22: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_all_indexed_vertices") static function kinc_graphics_draw_all_indexed_vertices(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_indexed_vertices") static function kinc_graphics_draw_indexed_vertices(start: Int, count: Int): Void {}
|
||||
|
||||
@:hlNative("std",
|
||||
"kinc_graphics_draw_all_indexed_vertices_instanced") static function kinc_graphics_draw_all_indexed_vertices_instanced(instanceCount: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_indexed_vertices_instanced") static function kinc_graphics_draw_indexed_vertices_instanced(instanceCount: Int,
|
||||
start: Int, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_restore_render_target") static function kinc_graphics_restore_render_target(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_texture") static function kinc_graphics_render_to_texture(renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_textures") static function kinc_graphics_render_to_textures(rt0: Pointer, rt1: Pointer, rt2: Pointer,
|
||||
rt3: Pointer, rt4: Pointer, rt5: Pointer, rt6: Pointer, rt7: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_face") static function kinc_graphics_render_to_face(renderTarget: Pointer, face: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_flush") static function kinc_graphics_flush(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_g4_set_compute_shader") static function kinc_g4_set_compute_shader(shader: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_g4_compute") static function kinc_g4_compute(x: Int, y: Int, z: Int): Void {}
|
||||
}
|
||||
package kha.korehl.graphics4;
|
||||
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.graphics4.ComputeShader;
|
||||
import kha.graphics4.CubeMap;
|
||||
import kha.graphics4.MipMapFilter;
|
||||
import kha.graphics4.PipelineState;
|
||||
import kha.graphics4.ShaderStorageBuffer;
|
||||
import kha.graphics4.TextureAddressing;
|
||||
import kha.graphics4.TextureFilter;
|
||||
import kha.graphics4.Usage;
|
||||
import kha.graphics4.VertexBuffer;
|
||||
import kha.math.FastMatrix3;
|
||||
import kha.math.FastMatrix4;
|
||||
import kha.math.FastVector2;
|
||||
import kha.math.FastVector3;
|
||||
import kha.math.FastVector4;
|
||||
import kha.Canvas;
|
||||
import kha.Image;
|
||||
import kha.Video;
|
||||
import kha.Color;
|
||||
|
||||
class Graphics implements kha.graphics4.Graphics {
|
||||
var target: Canvas;
|
||||
|
||||
public function new(target: Canvas = null) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public function vsynced(): Bool {
|
||||
return kinc_graphics_vsynced();
|
||||
}
|
||||
|
||||
public function refreshRate(): Int {
|
||||
return kinc_graphics_refreshrate();
|
||||
}
|
||||
|
||||
public function clear(?color: Color, ?z: FastFloat, ?stencil: Int): Void {
|
||||
var flags: Int = 0;
|
||||
if (color != null)
|
||||
flags |= 1;
|
||||
if (z != null)
|
||||
flags |= 2;
|
||||
if (stencil != null)
|
||||
flags |= 4;
|
||||
kinc_graphics_clear(flags, color == null ? 0 : color.value, z, stencil);
|
||||
}
|
||||
|
||||
public function viewport(x: Int, y: Int, width: Int, height: Int): Void {
|
||||
kinc_graphics_viewport(x, y, width, height);
|
||||
}
|
||||
|
||||
public function setVertexBuffer(vertexBuffer: kha.graphics4.VertexBuffer): Void {
|
||||
kinc_graphics_set_vertexbuffer(vertexBuffer._buffer);
|
||||
}
|
||||
|
||||
public function setVertexBuffers(vertexBuffers: Array<kha.graphics4.VertexBuffer>): Void {
|
||||
kinc_graphics_set_vertexbuffers(vertexBuffers.length > 0 ? vertexBuffers[0]._buffer : null,
|
||||
vertexBuffers.length > 1 ? vertexBuffers[1]._buffer : null, vertexBuffers.length > 2 ? vertexBuffers[2]._buffer : null,
|
||||
vertexBuffers.length > 3 ? vertexBuffers[3]._buffer : null, vertexBuffers.length);
|
||||
}
|
||||
|
||||
public function setIndexBuffer(indexBuffer: kha.graphics4.IndexBuffer): Void {
|
||||
kinc_graphics_set_indexbuffer(indexBuffer._buffer);
|
||||
}
|
||||
|
||||
public function maxTextureSize(): Int {
|
||||
return 4096;
|
||||
}
|
||||
|
||||
public function supportsNonPow2Textures(): Bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function setCubeMap(unit: kha.graphics4.TextureUnit, cubeMap: kha.graphics4.CubeMap): Void {
|
||||
if (cubeMap == null)
|
||||
return;
|
||||
if (cubeMap._texture != null)
|
||||
kinc_graphics_set_cubemap_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._texture);
|
||||
else
|
||||
kinc_graphics_set_cubemap_target(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._renderTarget);
|
||||
}
|
||||
|
||||
public function setCubeMapDepth(unit: kha.graphics4.TextureUnit, cubeMap: kha.graphics4.CubeMap): Void {
|
||||
if (cubeMap == null)
|
||||
return;
|
||||
kinc_graphics_set_cubemap_depth(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, cubeMap._renderTarget);
|
||||
}
|
||||
|
||||
public function scissor(x: Int, y: Int, width: Int, height: Int): Void {
|
||||
kinc_graphics_scissor(x, y, width, height);
|
||||
}
|
||||
|
||||
public function disableScissor(): Void {
|
||||
kinc_graphics_disable_scissor();
|
||||
}
|
||||
|
||||
public function instancedRenderingAvailable(): Bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function setTextureParameters(unit: kha.graphics4.TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void {
|
||||
kinc_graphics_set_texture_parameters(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, uAddressing, vAddressing, minificationFilter,
|
||||
magnificationFilter, mipmapFilter);
|
||||
}
|
||||
|
||||
public function setTexture3DParameters(unit: kha.graphics4.TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
wAddressing: TextureAddressing, minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void {
|
||||
kinc_graphics_set_texture3d_parameters(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, uAddressing, vAddressing, wAddressing, minificationFilter,
|
||||
magnificationFilter, mipmapFilter);
|
||||
}
|
||||
|
||||
public function setTextureCompareMode(unit: kha.graphics4.TextureUnit, enabled: Bool) {
|
||||
kinc_graphics_set_texture_compare_mode(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, enabled);
|
||||
}
|
||||
|
||||
public function setCubeMapCompareMode(unit: kha.graphics4.TextureUnit, enabled: Bool) {
|
||||
kinc_graphics_set_cube_map_compare_mode(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, enabled);
|
||||
}
|
||||
|
||||
public function setTexture(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
if (texture._texture != null)
|
||||
kinc_graphics_set_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._texture);
|
||||
else
|
||||
kinc_graphics_set_render_target(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._renderTarget);
|
||||
}
|
||||
|
||||
public function setTextureArray(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture_array(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._textureArray);
|
||||
}
|
||||
|
||||
public function setTextureDepth(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture_depth(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._renderTarget);
|
||||
}
|
||||
|
||||
public function setVideoTexture(unit: kha.graphics4.TextureUnit, texture: kha.Video): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
kinc_graphics_set_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, Image.fromVideo(texture)._texture);
|
||||
}
|
||||
|
||||
public function setImageTexture(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
kinc_graphics_set_image_texture(cast(unit, kha.korehl.graphics4.TextureUnit)._unit, texture._texture);
|
||||
}
|
||||
|
||||
public function maxBoundTextures(): Int {
|
||||
return 8;
|
||||
}
|
||||
|
||||
public function setPipeline(pipe: PipelineState): Void {
|
||||
pipe.set();
|
||||
}
|
||||
|
||||
public function setStencilReferenceValue(value: Int): Void {}
|
||||
|
||||
public function setBool(location: kha.graphics4.ConstantLocation, value: Bool): Void {
|
||||
kinc_graphics_set_bool(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setInt(location: kha.graphics4.ConstantLocation, value: Int): Void {
|
||||
kinc_graphics_set_int(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setInt2(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int): Void {
|
||||
kinc_graphics_set_int2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2);
|
||||
}
|
||||
|
||||
public function setInt3(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int, value3: Int): Void {
|
||||
kinc_graphics_set_int3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3);
|
||||
}
|
||||
|
||||
public function setInt4(location: kha.graphics4.ConstantLocation, value1: Int, value2: Int, value3: Int, value4: Int): Void {
|
||||
kinc_graphics_set_int4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3, value4);
|
||||
}
|
||||
|
||||
public function setInts(location: kha.graphics4.ConstantLocation, values: kha.arrays.Int32Array): Void {
|
||||
kinc_graphics_set_ints(cast(location, kha.korehl.graphics4.ConstantLocation)._location, values.getData(), values.length);
|
||||
}
|
||||
|
||||
public function setFloat(location: kha.graphics4.ConstantLocation, value: FastFloat): Void {
|
||||
kinc_graphics_set_float(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value);
|
||||
}
|
||||
|
||||
public function setFloat2(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat): Void {
|
||||
kinc_graphics_set_float2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2);
|
||||
}
|
||||
|
||||
public function setFloat3(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat): Void {
|
||||
kinc_graphics_set_float3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3);
|
||||
}
|
||||
|
||||
public function setFloat4(location: kha.graphics4.ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat, value4: FastFloat): Void {
|
||||
kinc_graphics_set_float4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value1, value2, value3, value4);
|
||||
}
|
||||
|
||||
public function setVector2(location: kha.graphics4.ConstantLocation, value: FastVector2): Void {
|
||||
kinc_graphics_set_float2(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y);
|
||||
}
|
||||
|
||||
public function setVector3(location: kha.graphics4.ConstantLocation, value: FastVector3): Void {
|
||||
kinc_graphics_set_float3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y, value.z);
|
||||
}
|
||||
|
||||
public function setVector4(location: kha.graphics4.ConstantLocation, value: FastVector4): Void {
|
||||
kinc_graphics_set_float4(cast(location, kha.korehl.graphics4.ConstantLocation)._location, value.x, value.y, value.z, value.w);
|
||||
}
|
||||
|
||||
public function setFloats(location: kha.graphics4.ConstantLocation, values: Float32Array): Void {
|
||||
kinc_graphics_set_floats(cast(location, kha.korehl.graphics4.ConstantLocation)._location, values.getData(), values.length);
|
||||
}
|
||||
|
||||
public inline function setMatrix(location: kha.graphics4.ConstantLocation, matrix: FastMatrix4): Void {
|
||||
kinc_graphics_set_matrix(cast(location, kha.korehl.graphics4.ConstantLocation)._location, matrix._00, matrix._10, matrix._20, matrix._30, matrix._01,
|
||||
matrix._11, matrix._21, matrix._31, matrix._02, matrix._12, matrix._22, matrix._32, matrix._03, matrix._13, matrix._23, matrix._33);
|
||||
}
|
||||
|
||||
public inline function setMatrix3(location: kha.graphics4.ConstantLocation, matrix: FastMatrix3): Void {
|
||||
kinc_graphics_set_matrix3(cast(location, kha.korehl.graphics4.ConstantLocation)._location, matrix._00, matrix._10, matrix._20, matrix._01, matrix._11,
|
||||
matrix._21, matrix._02, matrix._12, matrix._22);
|
||||
}
|
||||
|
||||
public function drawIndexedVertices(start: Int = 0, count: Int = -1): Void {
|
||||
if (count < 0)
|
||||
kinc_graphics_draw_all_indexed_vertices();
|
||||
else
|
||||
kinc_graphics_draw_indexed_vertices(start, count);
|
||||
}
|
||||
|
||||
public function drawIndexedVerticesInstanced(instanceCount: Int, start: Int = 0, count: Int = -1): Void {
|
||||
if (count < 0)
|
||||
kinc_graphics_draw_all_indexed_vertices_instanced(instanceCount);
|
||||
else
|
||||
kinc_graphics_draw_indexed_vertices_instanced(instanceCount, start, count);
|
||||
}
|
||||
|
||||
function renderToTexture(additionalRenderTargets: Array<Canvas>): Void {
|
||||
if (additionalRenderTargets != null) {
|
||||
var len = additionalRenderTargets.length;
|
||||
var rt0 = cast(target, Image)._renderTarget;
|
||||
var rt1 = len > 0 ? cast(additionalRenderTargets[0], Image)._renderTarget : null;
|
||||
var rt2 = len > 1 ? cast(additionalRenderTargets[1], Image)._renderTarget : null;
|
||||
var rt3 = len > 2 ? cast(additionalRenderTargets[2], Image)._renderTarget : null;
|
||||
var rt4 = len > 3 ? cast(additionalRenderTargets[3], Image)._renderTarget : null;
|
||||
var rt5 = len > 4 ? cast(additionalRenderTargets[4], Image)._renderTarget : null;
|
||||
var rt6 = len > 5 ? cast(additionalRenderTargets[5], Image)._renderTarget : null;
|
||||
var rt7 = len > 6 ? cast(additionalRenderTargets[6], Image)._renderTarget : null;
|
||||
kinc_graphics_render_to_textures(rt0, rt1, rt2, rt3, rt4, rt5, rt6, rt7, len + 1);
|
||||
}
|
||||
else {
|
||||
kinc_graphics_render_to_texture(cast(target, Image)._renderTarget);
|
||||
}
|
||||
}
|
||||
|
||||
public function begin(additionalRenderTargets: Array<Canvas> = null): Void {
|
||||
if (target == null)
|
||||
kinc_graphics_restore_render_target();
|
||||
else
|
||||
renderToTexture(additionalRenderTargets);
|
||||
}
|
||||
|
||||
public function beginFace(face: Int): Void {
|
||||
kinc_graphics_render_to_face((target is CubeMap) ? cast(target, CubeMap)._renderTarget : cast(target, Image)._renderTarget, face);
|
||||
}
|
||||
|
||||
public function beginEye(eye: Int): Void {}
|
||||
|
||||
public function end(): Void {}
|
||||
|
||||
public function flush(): Void {
|
||||
kinc_graphics_flush();
|
||||
}
|
||||
|
||||
public function setShaderStorageBuffer(buffer: ShaderStorageBuffer, index: Int) {
|
||||
// Kore::Compute::setBuffer(buffer->buffer, index);
|
||||
}
|
||||
|
||||
public function setComputeShader(shader: ComputeShader) {
|
||||
kinc_g4_set_compute_shader(shader._shader);
|
||||
}
|
||||
|
||||
public function compute(x: Int, y: Int, z: Int) {
|
||||
kinc_g4_compute(x, y, z);
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_clear") static function kinc_graphics_clear(flags: Int, color: Int, z: FastFloat, stencil: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_vsynced") static function kinc_graphics_vsynced(): Bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_refreshrate") static function kinc_graphics_refreshrate(): Int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_viewport") static function kinc_graphics_viewport(x: Int, y: Int, width: Int, height: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_vertexbuffer") static function kinc_graphics_set_vertexbuffer(buffer: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_vertexbuffers") static function kinc_graphics_set_vertexbuffers(b0: Pointer, b1: Pointer, b2: Pointer, b3: Pointer,
|
||||
count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_indexbuffer") static function kinc_graphics_set_indexbuffer(buffer: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_scissor") static function kinc_graphics_scissor(x: Int, y: Int, width: Int, height: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_disable_scissor") static function kinc_graphics_disable_scissor(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_parameters") static function kinc_graphics_set_texture_parameters(unit: Pointer, uAddressing: Int,
|
||||
vAddressing: Int, minificationFilter: Int, magnificationFilter: Int, mipmapFilter: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture3d_parameters") static function kinc_graphics_set_texture3d_parameters(unit: Pointer, uAddressing: Int,
|
||||
vAddressing: Int, wAddressing: Int, minificationFilter: Int, magnificationFilter: Int, mipmapFilter: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_compare_mode") static function kinc_graphics_set_texture_compare_mode(unit: Pointer, enabled: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cube_map_compare_mode") static function kinc_graphics_set_cube_map_compare_mode(unit: Pointer, enabled: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture") static function kinc_graphics_set_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_depth") static function kinc_graphics_set_texture_depth(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_texture_array") static function kinc_graphics_set_texture_array(unit: Pointer, textureArray: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_render_target") static function kinc_graphics_set_render_target(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_texture") static function kinc_graphics_set_cubemap_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_target") static function kinc_graphics_set_cubemap_target(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_cubemap_depth") static function kinc_graphics_set_cubemap_depth(unit: Pointer, renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_image_texture") static function kinc_graphics_set_image_texture(unit: Pointer, texture: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_bool") static function kinc_graphics_set_bool(location: Pointer, value: Bool): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int") static function kinc_graphics_set_int(location: Pointer, value: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int2") static function kinc_graphics_set_int2(location: Pointer, value1: Int, value2: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int3") static function kinc_graphics_set_int3(location: Pointer, value1: Int, value2: Int, value3: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_int4") static function kinc_graphics_set_int4(location: Pointer, value1: Int, value2: Int, value3: Int,
|
||||
value4: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_ints") static function kinc_graphics_set_ints(location: Pointer, values: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float") static function kinc_graphics_set_float(location: Pointer, value: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float2") static function kinc_graphics_set_float2(location: Pointer, value1: FastFloat, value2: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float3") static function kinc_graphics_set_float3(location: Pointer, value1: FastFloat, value2: FastFloat,
|
||||
value3: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_float4") static function kinc_graphics_set_float4(location: Pointer, value1: FastFloat, value2: FastFloat,
|
||||
value3: FastFloat, value4: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_floats") static function kinc_graphics_set_floats(location: Pointer, values: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_matrix") static function kinc_graphics_set_matrix(location: Pointer, _00: FastFloat, _10: FastFloat, _20: FastFloat,
|
||||
_30: FastFloat, _01: FastFloat, _11: FastFloat, _21: FastFloat, _31: FastFloat, _02: FastFloat, _12: FastFloat, _22: FastFloat, _32: FastFloat,
|
||||
_03: FastFloat, _13: FastFloat, _23: FastFloat, _33: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_set_matrix3") static function kinc_graphics_set_matrix3(location: Pointer, _00: FastFloat, _10: FastFloat,
|
||||
_20: FastFloat, _01: FastFloat, _11: FastFloat, _21: FastFloat, _02: FastFloat, _12: FastFloat, _22: FastFloat): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_all_indexed_vertices") static function kinc_graphics_draw_all_indexed_vertices(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_indexed_vertices") static function kinc_graphics_draw_indexed_vertices(start: Int, count: Int): Void {}
|
||||
|
||||
@:hlNative("std",
|
||||
"kinc_graphics_draw_all_indexed_vertices_instanced") static function kinc_graphics_draw_all_indexed_vertices_instanced(instanceCount: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_draw_indexed_vertices_instanced") static function kinc_graphics_draw_indexed_vertices_instanced(instanceCount: Int,
|
||||
start: Int, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_restore_render_target") static function kinc_graphics_restore_render_target(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_texture") static function kinc_graphics_render_to_texture(renderTarget: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_textures") static function kinc_graphics_render_to_textures(rt0: Pointer, rt1: Pointer, rt2: Pointer,
|
||||
rt3: Pointer, rt4: Pointer, rt5: Pointer, rt6: Pointer, rt7: Pointer, count: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_render_to_face") static function kinc_graphics_render_to_face(renderTarget: Pointer, face: Int): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_graphics_flush") static function kinc_graphics_flush(): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_g4_set_compute_shader") static function kinc_g4_set_compute_shader(shader: Pointer): Void {}
|
||||
|
||||
@:hlNative("std", "kinc_g4_compute") static function kinc_g4_compute(x: Int, y: Int, z: Int): Void {}
|
||||
}
|
||||
|
||||
@ -234,6 +234,8 @@ class Image implements Canvas implements Resource {
|
||||
return 5;
|
||||
case A16:
|
||||
return 7;
|
||||
case R32UI:
|
||||
return 8;
|
||||
default:
|
||||
return 1; // Grey8
|
||||
}
|
||||
|
||||
@ -23,6 +23,7 @@ extern class Krom {
|
||||
static function setRenderTarget(stage: kha.graphics4.TextureUnit, renderTarget: Dynamic): Void;
|
||||
static function setTextureDepth(unit: kha.graphics4.TextureUnit, texture: Dynamic): Void;
|
||||
static function setImageTexture(stage: kha.graphics4.TextureUnit, texture: Dynamic): Void;
|
||||
static function setImageRenderTarget(stage: kha.graphics4.TextureUnit, renderTarget: Dynamic): Void;
|
||||
static function setTextureParameters(texunit: kha.graphics4.TextureUnit, uAddressing: Int, vAddressing: Int, minificationFilter: Int,
|
||||
magnificationFilter: Int, mipmapFilter: Int): Void;
|
||||
static function setTexture3DParameters(texunit: kha.graphics4.TextureUnit, uAddressing: Int, vAddressing: Int, wAddressing: Int, minificationFilter: Int,
|
||||
@ -114,6 +115,7 @@ extern class Krom {
|
||||
static function windowWidth(id: Int): Int;
|
||||
static function windowHeight(id: Int): Int;
|
||||
static function setWindowTitle(id: Int, title: String): Void;
|
||||
static function windowSetForeground(id: Int): Void;
|
||||
static function screenDpi(): Int;
|
||||
static function systemId(): String;
|
||||
static function requestShutdown(): Void;
|
||||
@ -128,6 +130,11 @@ extern class Krom {
|
||||
static function readStorage(name: String): haxe.io.BytesData;
|
||||
|
||||
static function fileSaveBytes(path: String, bytes: haxe.io.BytesData): Void;
|
||||
static function fileWriteBytes(path: String, bytes: haxe.io.BytesData, ?offset: Float, ?byteLength: Int): Void;
|
||||
static function fileReadBytes(path: String, ?offset: Float, ?length: Int): haxe.io.BytesData;
|
||||
static function fileSize(path: String): Float;
|
||||
static function fileExists(path: String): Bool;
|
||||
static function deleteFile(path: String): Void;
|
||||
static function sysCommand(cmd: String, ?args: Array<String>): Int;
|
||||
static function savePath(): String;
|
||||
static function getArgCount(): Int;
|
||||
@ -158,4 +165,45 @@ extern class Krom {
|
||||
static function getConstantLocationCompute(shader: Dynamic, name: String): Dynamic;
|
||||
static function getTextureUnitCompute(shader: Dynamic, name: String): Dynamic;
|
||||
static function compute(x: Int, y: Int, z: Int): Void;
|
||||
static function webviewCreate(options: Dynamic): Int;
|
||||
static function webviewLoadHTML(id: Int, html: String): Void;
|
||||
static function webviewLoadURL(id: Int, url: String): Void;
|
||||
static function webviewEvalJS(id: Int, js: String): Void;
|
||||
static function webviewEvalJSAsync(id: Int, js: String, callback: String->Void): Void;
|
||||
static function webviewShow(id: Int): Void;
|
||||
static function webviewHide(id: Int): Void;
|
||||
static function webviewDestroy(id: Int): Void;
|
||||
static function webviewResize(id: Int, width: Int, height: Int): Void;
|
||||
static function webviewMove(id: Int, x: Int, y: Int): Void;
|
||||
static function webviewSetBounds(id: Int, x: Int, y: Int, width: Int, height: Int): Void;
|
||||
static function webviewGetX(id: Int): Int;
|
||||
static function webviewGetY(id: Int): Int;
|
||||
static function webviewGetWidth(id: Int): Int;
|
||||
static function webviewGetHeight(id: Int): Int;
|
||||
static function webviewSetTransparent(id: Int, transparent: Bool): Void;
|
||||
static function webviewSetClickThrough(id: Int, enabled: Bool): Void;
|
||||
static function webviewSetTitle(id: Int, title: String): Void;
|
||||
static function webviewSend(id: Int, message: String): Void;
|
||||
static function webviewSetOnMessage(id: Int, callback: String->Void): Void;
|
||||
static function webviewSetOnLoad(id: Int, callback: Void->Void): Void;
|
||||
static function webviewSetOnError(id: Int, callback: String->Void): Void;
|
||||
static function webviewSetOnClose(id: Int, callback: Void->Void): Void;
|
||||
static function webviewCount(): Int;
|
||||
static function webviewIsValid(id: Int): Bool;
|
||||
static function webviewSetActiveDOM(id: Int): Void;
|
||||
static function webviewGetActiveDOM(): Int;
|
||||
static function webviewGoBack(id: Int): Void;
|
||||
static function webviewGoForward(id: Int): Void;
|
||||
static function webviewReload(id: Int): Void;
|
||||
static function webviewCanGoBack(id: Int): Bool;
|
||||
static function webviewCanGoForward(id: Int): Bool;
|
||||
static function webviewGetURL(id: Int): String;
|
||||
static function webviewGetPageTitle(id: Int): String;
|
||||
static function webviewMinimize(id: Int): Void;
|
||||
static function webviewMaximize(id: Int): Void;
|
||||
static function webviewRestore(id: Int): Void;
|
||||
static function webviewSetFullscreen(id: Int, fullscreen: Bool): Void;
|
||||
static function webviewIsFullscreen(id: Int): Bool;
|
||||
static function webviewEnableDevTools(id: Int, enabled: Bool): Void;
|
||||
static function webviewSetContextMenu(id: Int, enabled: Bool): Void;
|
||||
}
|
||||
|
||||
@ -75,6 +75,8 @@ class Image implements Canvas implements Resource {
|
||||
return 5;
|
||||
case A16:
|
||||
return 7;
|
||||
case R32UI:
|
||||
return 8;
|
||||
default:
|
||||
return 1; // Grey8
|
||||
}
|
||||
@ -200,6 +202,7 @@ class Image implements Canvas implements Resource {
|
||||
case RGBA64: 8;
|
||||
case A32: 4;
|
||||
case A16: 2;
|
||||
case R32UI: 4;
|
||||
default: 4;
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,15 +30,15 @@ class LoaderImpl {
|
||||
}
|
||||
|
||||
public static function loadSoundFromDescription(desc: Dynamic, done: kha.Sound->Void, failed: AssetError->Void) {
|
||||
var sound = Krom.loadSound(desc.files[0]);
|
||||
if (sound == null) {
|
||||
var sound = new kha.krom.Sound(desc.files[0]);
|
||||
if (sound.uncompressedData == null) {
|
||||
failed({
|
||||
url: desc.files.join(","),
|
||||
error: "Could not load sound(s)",
|
||||
});
|
||||
}
|
||||
else {
|
||||
done(new kha.krom.Sound(Bytes.ofData(sound)));
|
||||
done(sound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -106,7 +106,7 @@ class Compute {
|
||||
public static function setSampledDepthTexture(unit: TextureUnit, texture: Image) {
|
||||
if (texture == null)
|
||||
return;
|
||||
Krom.setSampledDepthTextureCompute(unit, texture);
|
||||
Krom.setSampledDepthTextureCompute(unit, texture.renderTarget_);
|
||||
}
|
||||
|
||||
public static function setSampledCubeMap(unit: TextureUnit, cubeMap: CubeMap) {
|
||||
@ -118,7 +118,7 @@ class Compute {
|
||||
public static function setSampledDepthCubeMap(unit: TextureUnit, cubeMap: CubeMap) {
|
||||
if (cubeMap == null)
|
||||
return;
|
||||
Krom.setSampledDepthTextureCompute(unit, cubeMap);
|
||||
Krom.setSampledDepthTextureCompute(unit, cubeMap.renderTarget_);
|
||||
}
|
||||
|
||||
public static function setTextureParameters(unit: TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
|
||||
@ -9,14 +9,7 @@ class ShaderStorageBuffer {
|
||||
public function new(indexCount: Int, type: VertexData) {
|
||||
myCount = indexCount;
|
||||
data = new Array<Int>();
|
||||
data[myCount - 1] = 0;
|
||||
init(indexCount, type);
|
||||
}
|
||||
|
||||
function init(indexCount: Int, type: VertexData) {
|
||||
myCount = indexCount;
|
||||
data = new Array<Int>();
|
||||
data[myCount - 1] = 0;
|
||||
if (myCount > 0) data[myCount - 1] = 0;
|
||||
}
|
||||
|
||||
public function delete(): Void {}
|
||||
|
||||
@ -1,22 +1,25 @@
|
||||
package kha.graphics4;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import kha.Blob;
|
||||
|
||||
class ComputeShader {
|
||||
public function new(sources: Array<Blob>, files: Array<String>) {
|
||||
|
||||
}
|
||||
|
||||
public function delete(): Void {
|
||||
|
||||
}
|
||||
|
||||
public function getConstantLocation(name: String): ConstantLocation {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getTextureUnit(name: String): TextureUnit {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
package kha.graphics4;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
import kha.Blob;
|
||||
|
||||
class ComputeShader {
|
||||
public var shader_: Dynamic;
|
||||
|
||||
public function new(sources: Array<Blob>, files: Array<String>) {
|
||||
shader_ = Krom.createShaderCompute(sources[0].toBytes().getData());
|
||||
}
|
||||
|
||||
public function delete(): Void {
|
||||
Krom.deleteShaderCompute(shader_);
|
||||
shader_ = null;
|
||||
}
|
||||
|
||||
public function getConstantLocation(name: String): ConstantLocation {
|
||||
return Krom.getConstantLocationCompute(shader_, name);
|
||||
}
|
||||
|
||||
public function getTextureUnit(name: String): TextureUnit {
|
||||
return Krom.getTextureUnitCompute(shader_, name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -122,7 +122,12 @@ class Graphics implements kha.graphics4.Graphics {
|
||||
public function setImageTexture(unit: kha.graphics4.TextureUnit, texture: kha.Image): Void {
|
||||
if (texture == null)
|
||||
return;
|
||||
Krom.setImageTexture(unit, texture.texture_);
|
||||
if (texture.texture_ != null) {
|
||||
Krom.setImageTexture(unit, texture.texture_);
|
||||
}
|
||||
else if (texture.renderTarget_ != null) {
|
||||
Krom.setImageRenderTarget(unit, texture.renderTarget_);
|
||||
}
|
||||
}
|
||||
|
||||
public function setTextureParameters(texunit: kha.graphics4.TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
@ -261,10 +266,10 @@ class Graphics implements kha.graphics4.Graphics {
|
||||
}
|
||||
|
||||
public function setComputeShader(shader: ComputeShader) {
|
||||
|
||||
Krom.setShaderCompute(shader.shader_);
|
||||
}
|
||||
|
||||
public function compute(x: Int, y: Int, z: Int) {
|
||||
|
||||
Krom.compute(x, y, z);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,24 +2,28 @@ package kha.krom;
|
||||
|
||||
import haxe.io.Bytes;
|
||||
|
||||
using StringTools;
|
||||
|
||||
class Sound extends kha.Sound {
|
||||
public function new(bytes: Bytes) {
|
||||
public function new(filename: String) {
|
||||
super();
|
||||
|
||||
var count = Std.int(bytes.length / 4);
|
||||
uncompressedData = new kha.arrays.Float32Array(count);
|
||||
for (i in 0...count) {
|
||||
uncompressedData[i] = bytes.getFloat(i * 4);
|
||||
}
|
||||
var sound = Krom.loadSound(filename);
|
||||
if (sound != null) {
|
||||
var bytes = Bytes.ofData(sound.buffer);
|
||||
var count = Std.int(bytes.length / 4);
|
||||
uncompressedData = new kha.arrays.Float32Array(count);
|
||||
for (i in 0...count) {
|
||||
uncompressedData[i] = bytes.getFloat(i * 4);
|
||||
}
|
||||
|
||||
compressedData = null;
|
||||
this.sampleRate = sound.sampleRate;
|
||||
this.channels = sound.channels;
|
||||
this.length = sound.length;
|
||||
}
|
||||
}
|
||||
|
||||
override public function uncompress(done: Void->Void): Void {
|
||||
done();
|
||||
}
|
||||
|
||||
override public function unload(): Void {
|
||||
super.unload();
|
||||
}
|
||||
}
|
||||
|
||||
@ -821,8 +821,19 @@ int kinc_g4_max_bound_textures(void) {
|
||||
return units;
|
||||
}
|
||||
|
||||
static int getUnitStage(kinc_g4_texture_unit_t unit) {
|
||||
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
|
||||
if (unit.stages[i] >= 0) {
|
||||
return unit.stages[i];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void setTextureAddressingInternal(GLenum target, kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
int stage = getUnitStage(unit);
|
||||
if (stage < 0) return;
|
||||
glActiveTexture(GL_TEXTURE0 + stage);
|
||||
GLenum texDir;
|
||||
switch (dir) {
|
||||
case KINC_G4_TEXTURE_DIRECTION_U:
|
||||
@ -841,39 +852,39 @@ static void setTextureAddressingInternal(GLenum target, kinc_g4_texture_unit_t u
|
||||
case KINC_G4_TEXTURE_ADDRESSING_CLAMP:
|
||||
glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE);
|
||||
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
||||
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
||||
texModesU[stage] = GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
else {
|
||||
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
||||
texModesV[stage] = GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
break;
|
||||
case KINC_G4_TEXTURE_ADDRESSING_REPEAT:
|
||||
glTexParameteri(target, texDir, GL_REPEAT);
|
||||
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
||||
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
||||
texModesU[stage] = GL_REPEAT;
|
||||
}
|
||||
else {
|
||||
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
||||
texModesV[stage] = GL_REPEAT;
|
||||
}
|
||||
break;
|
||||
case KINC_G4_TEXTURE_ADDRESSING_BORDER:
|
||||
// unsupported
|
||||
glTexParameteri(target, texDir, GL_CLAMP_TO_EDGE);
|
||||
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
||||
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
||||
texModesU[stage] = GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
else {
|
||||
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_CLAMP_TO_EDGE;
|
||||
texModesV[stage] = GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
break;
|
||||
case KINC_G4_TEXTURE_ADDRESSING_MIRROR:
|
||||
// unsupported
|
||||
glTexParameteri(target, texDir, GL_REPEAT);
|
||||
if (dir == KINC_G4_TEXTURE_DIRECTION_U) {
|
||||
texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
||||
texModesU[stage] = GL_REPEAT;
|
||||
}
|
||||
else {
|
||||
texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = GL_REPEAT;
|
||||
texModesV[stage] = GL_REPEAT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -881,11 +892,15 @@ static void setTextureAddressingInternal(GLenum target, kinc_g4_texture_unit_t u
|
||||
}
|
||||
|
||||
int Kinc_G4_Internal_TextureAddressingU(kinc_g4_texture_unit_t unit) {
|
||||
return texModesU[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]];
|
||||
int stage = getUnitStage(unit);
|
||||
if (stage < 0) return GL_CLAMP_TO_EDGE;
|
||||
return texModesU[stage];
|
||||
}
|
||||
|
||||
int Kinc_G4_Internal_TextureAddressingV(kinc_g4_texture_unit_t unit) {
|
||||
return texModesV[unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]];
|
||||
int stage = getUnitStage(unit);
|
||||
if (stage < 0) return GL_CLAMP_TO_EDGE;
|
||||
return texModesV[stage];
|
||||
}
|
||||
|
||||
void kinc_g4_set_texture_addressing(kinc_g4_texture_unit_t unit, kinc_g4_texture_direction_t dir, kinc_g4_texture_addressing_t addressing) {
|
||||
@ -899,7 +914,9 @@ void kinc_g4_set_texture3d_addressing(kinc_g4_texture_unit_t unit, kinc_g4_textu
|
||||
}
|
||||
|
||||
static void setTextureMagnificationFilterInternal(GLenum target, kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
||||
glActiveTexture(GL_TEXTURE0 + texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
int stage = getUnitStage(texunit);
|
||||
if (stage < 0) return;
|
||||
glActiveTexture(GL_TEXTURE0 + stage);
|
||||
glCheckErrors();
|
||||
switch (filter) {
|
||||
case KINC_G4_TEXTURE_FILTER_POINT:
|
||||
@ -964,26 +981,34 @@ static void setMinMipFilters(GLenum target, int unit) {
|
||||
}
|
||||
|
||||
void kinc_g4_set_texture_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
||||
minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
||||
setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
int stage = getUnitStage(texunit);
|
||||
if (stage < 0) return;
|
||||
minFilters[stage] = filter;
|
||||
setMinMipFilters(GL_TEXTURE_2D, stage);
|
||||
}
|
||||
|
||||
void kinc_g4_set_texture3d_minification_filter(kinc_g4_texture_unit_t texunit, kinc_g4_texture_filter_t filter) {
|
||||
minFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
||||
int stage = getUnitStage(texunit);
|
||||
if (stage < 0) return;
|
||||
minFilters[stage] = filter;
|
||||
#ifndef KINC_OPENGL_ES
|
||||
setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
setMinMipFilters(GL_TEXTURE_3D, stage);
|
||||
#endif
|
||||
}
|
||||
|
||||
void kinc_g4_set_texture_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) {
|
||||
mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
||||
setMinMipFilters(GL_TEXTURE_2D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
int stage = getUnitStage(texunit);
|
||||
if (stage < 0) return;
|
||||
mipFilters[stage] = filter;
|
||||
setMinMipFilters(GL_TEXTURE_2D, stage);
|
||||
}
|
||||
|
||||
void kinc_g4_set_texture3d_mipmap_filter(kinc_g4_texture_unit_t texunit, kinc_g4_mipmap_filter_t filter) {
|
||||
mipFilters[texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]] = filter;
|
||||
int stage = getUnitStage(texunit);
|
||||
if (stage < 0) return;
|
||||
mipFilters[stage] = filter;
|
||||
#ifndef KINC_OPENGL_ES
|
||||
setMinMipFilters(GL_TEXTURE_3D, texunit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
setMinMipFilters(GL_TEXTURE_3D, stage);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@ -190,6 +190,17 @@ void kinc_g4_set_compute_shader(kinc_g4_compute_shader *shader) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void kinc_g4_set_image_render_target(kinc_g4_texture_unit_t unit, kinc_g4_render_target_t *render_target) {
|
||||
#if defined(KINC_WINDOWS) || (defined(KINC_LINUX) && defined(GL_VERSION_4_4))
|
||||
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
|
||||
if (unit.stages[i] >= 0) {
|
||||
glBindImageTexture(unit.stages[i], render_target->impl._texture, 0, GL_FALSE, 0, GL_READ_WRITE, convertInternalRTFormat((kinc_g4_render_target_format_t)render_target->impl.format));
|
||||
}
|
||||
}
|
||||
glCheckErrors();
|
||||
#endif
|
||||
}
|
||||
|
||||
void kinc_g4_compute(int x, int y, int z) {
|
||||
#ifdef HAS_COMPUTE
|
||||
glDispatchCompute(x, y, z);
|
||||
|
||||
@ -360,17 +360,23 @@ void kinc_g4_render_target_destroy(kinc_g4_render_target_t *renderTarget) {
|
||||
}
|
||||
|
||||
void kinc_g4_render_target_use_color_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
glCheckErrors();
|
||||
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._texture);
|
||||
glCheckErrors();
|
||||
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
|
||||
if (unit.stages[i] >= 0) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit.stages[i]);
|
||||
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._texture);
|
||||
glCheckErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void kinc_g4_render_target_use_depth_as_texture(kinc_g4_render_target_t *renderTarget, kinc_g4_texture_unit_t unit) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit.stages[KINC_G4_SHADER_TYPE_FRAGMENT]);
|
||||
glCheckErrors();
|
||||
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture);
|
||||
glCheckErrors();
|
||||
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
|
||||
if (unit.stages[i] >= 0) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit.stages[i]);
|
||||
glBindTexture(renderTarget->isCubeMap ? GL_TEXTURE_CUBE_MAP : GL_TEXTURE_2D, renderTarget->impl._depthTexture);
|
||||
glCheckErrors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void kinc_g4_render_target_set_depth_stencil_from(kinc_g4_render_target_t *renderTarget, kinc_g4_render_target_t *source) {
|
||||
|
||||
@ -99,6 +99,8 @@ static int convertFormat(kinc_image_format_t format) {
|
||||
case KINC_IMAGE_FORMAT_A16:
|
||||
case KINC_IMAGE_FORMAT_GREY8:
|
||||
return GL_RED;
|
||||
case KINC_IMAGE_FORMAT_R32UI:
|
||||
return GL_RED_INTEGER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +134,8 @@ static int convertInternalFormat(kinc_image_format_t format) {
|
||||
#else
|
||||
return GL_R8;
|
||||
#endif
|
||||
case KINC_IMAGE_FORMAT_R32UI:
|
||||
return GL_R32UI;
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,6 +149,8 @@ static int convertType(kinc_image_format_t format) {
|
||||
case KINC_IMAGE_FORMAT_RGBA32:
|
||||
default:
|
||||
return GL_UNSIGNED_BYTE;
|
||||
case KINC_IMAGE_FORMAT_R32UI:
|
||||
return GL_UNSIGNED_INT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,7 +489,7 @@ void kinc_g4_texture_init3d(kinc_g4_texture_t *texture, int width, int height, i
|
||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
glCheckErrors();
|
||||
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, convertInternalFormat(format), width, height, depth, 0, convertFormat(format), GL_UNSIGNED_BYTE, NULL);
|
||||
glTexImage3D(GL_TEXTURE_3D, 0, convertInternalFormat(format), width, height, depth, 0, convertFormat(format), convertType(format), NULL);
|
||||
glCheckErrors();
|
||||
#endif
|
||||
}
|
||||
@ -523,8 +529,8 @@ void Kinc_G4_Internal_TextureSet(kinc_g4_texture_t *texture, kinc_g4_texture_uni
|
||||
#else
|
||||
glBindTexture(target, texture->impl.texture);
|
||||
glCheckErrors();
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, Kinc_G4_Internal_TextureAddressingU(unit));
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, Kinc_G4_Internal_TextureAddressingV(unit));
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, Kinc_G4_Internal_TextureAddressingU(unit));
|
||||
glTexParameteri(target, GL_TEXTURE_WRAP_T, Kinc_G4_Internal_TextureAddressingV(unit));
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -532,7 +538,7 @@ void Kinc_G4_Internal_TextureImageSet(kinc_g4_texture_t *texture, kinc_g4_textur
|
||||
#if defined(KINC_WINDOWS) || (defined(KINC_LINUX) && defined(GL_VERSION_4_4))
|
||||
for (int i = 0; i < KINC_G4_SHADER_TYPE_COUNT; ++i) {
|
||||
if (unit.stages[i] >= 0) {
|
||||
glBindImageTexture(unit.stages[i], texture->impl.texture, 0, GL_FALSE, 0, GL_WRITE_ONLY, convertInternalFormat(texture->format));
|
||||
glBindImageTexture(unit.stages[i], texture->impl.texture, 0, GL_FALSE, 0, GL_READ_WRITE, convertInternalFormat(texture->format));
|
||||
}
|
||||
}
|
||||
glCheckErrors();
|
||||
|
||||
@ -392,6 +392,11 @@ void kinc_window_hide(int window_index) {
|
||||
UpdateWindow(windows[window_index].handle);
|
||||
}
|
||||
|
||||
void kinc_window_set_foreground(int window_index) {
|
||||
SetForegroundWindow(windows[window_index].handle);
|
||||
SetFocus(windows[window_index].handle);
|
||||
}
|
||||
|
||||
void kinc_window_set_title(int window_index, const char *title) {
|
||||
wchar_t buffer[1024];
|
||||
MultiByteToWideChar(CP_UTF8, 0, title, -1, buffer, 1024);
|
||||
|
||||
@ -376,6 +376,8 @@ KINC_FUNC void kinc_g4_set_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_t
|
||||
/// <param name="texture">The texture to assign to the unit</param>
|
||||
KINC_FUNC void kinc_g4_set_image_texture(kinc_g4_texture_unit_t unit, struct kinc_g4_texture *texture);
|
||||
|
||||
KINC_FUNC void kinc_g4_set_image_render_target(kinc_g4_texture_unit_t unit, struct kinc_g4_render_target *render_target);
|
||||
|
||||
KINC_FUNC bool kinc_g4_init_occlusion_query(unsigned *occlusionQuery);
|
||||
|
||||
KINC_FUNC void kinc_g4_delete_occlusion_query(unsigned occlusionQuery);
|
||||
|
||||
@ -30,7 +30,8 @@ typedef enum kinc_image_format {
|
||||
KINC_IMAGE_FORMAT_RGBA64,
|
||||
KINC_IMAGE_FORMAT_A32,
|
||||
KINC_IMAGE_FORMAT_BGRA32,
|
||||
KINC_IMAGE_FORMAT_A16
|
||||
KINC_IMAGE_FORMAT_A16,
|
||||
KINC_IMAGE_FORMAT_R32UI
|
||||
} kinc_image_format_t;
|
||||
|
||||
typedef struct kinc_image {
|
||||
@ -608,6 +609,8 @@ int kinc_image_format_sizeof(kinc_image_format_t format) {
|
||||
return 1;
|
||||
case KINC_IMAGE_FORMAT_RGB24:
|
||||
return 3;
|
||||
case KINC_IMAGE_FORMAT_R32UI:
|
||||
return 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -139,6 +139,11 @@ KINC_FUNC void kinc_window_show(int window);
|
||||
/// </summary>
|
||||
KINC_FUNC void kinc_window_hide(int window);
|
||||
|
||||
/// <summary>
|
||||
/// Brings a window to the foreground and sets focus to it.
|
||||
/// </summary>
|
||||
KINC_FUNC void kinc_window_set_foreground(int window);
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title of a window.
|
||||
/// </summary>
|
||||
|
||||
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 417 KiB After Width: | Height: | Size: 281 KiB |
@ -68,7 +68,7 @@ class Sound implements Resource {
|
||||
var soundBytes = output.getBytes();
|
||||
var count = Std.int(soundBytes.length / 4);
|
||||
if (header.channel == 1) {
|
||||
length = count / kha.audio2.Audio.samplesPerSecond; // header.sampleRate;
|
||||
length = count / header.sampleRate;
|
||||
uncompressedData = new kha.arrays.Float32Array(count * 2);
|
||||
for (i in 0...count) {
|
||||
uncompressedData[i * 2 + 0] = soundBytes.getFloat(i * 4);
|
||||
@ -76,7 +76,7 @@ class Sound implements Resource {
|
||||
}
|
||||
}
|
||||
else {
|
||||
length = count / 2 / kha.audio2.Audio.samplesPerSecond; // header.sampleRate;
|
||||
length = count / 2 / header.sampleRate;
|
||||
uncompressedData = new kha.arrays.Float32Array(count);
|
||||
for (i in 0...count) {
|
||||
uncompressedData[i] = soundBytes.getFloat(i * 4);
|
||||
|
||||
7
Kha/Sources/kha/compute/Access.hx
Normal file
@ -0,0 +1,7 @@
|
||||
package kha.compute;
|
||||
|
||||
enum abstract Access(Int) to Int {
|
||||
var Read = 0;
|
||||
var Write = 1;
|
||||
var ReadWrite = 2;
|
||||
}
|
||||
41
Kha/Sources/kha/compute/Compute.hx
Normal file
@ -0,0 +1,41 @@
|
||||
package kha.compute;
|
||||
|
||||
import kha.arrays.Float32Array;
|
||||
import kha.Image;
|
||||
import kha.FastFloat;
|
||||
import kha.math.FastMatrix3;
|
||||
import kha.math.FastMatrix4;
|
||||
import kha.math.FastVector2;
|
||||
import kha.math.FastVector3;
|
||||
import kha.math.FastVector4;
|
||||
import kha.graphics4.CubeMap;
|
||||
import kha.graphics4.TextureAddressing;
|
||||
import kha.graphics4.TextureFilter;
|
||||
import kha.graphics4.MipMapFilter;
|
||||
|
||||
extern class Compute {
|
||||
public static function setBool(location: ConstantLocation, value: Bool): Void;
|
||||
public static function setInt(location: ConstantLocation, value: Int): Void;
|
||||
public static function setFloat(location: ConstantLocation, value: FastFloat): Void;
|
||||
public static function setFloat2(location: ConstantLocation, value1: FastFloat, value2: FastFloat): Void;
|
||||
public static function setFloat3(location: ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat): Void;
|
||||
public static function setFloat4(location: ConstantLocation, value1: FastFloat, value2: FastFloat, value3: FastFloat, value4: FastFloat): Void;
|
||||
public static function setFloats(location: ConstantLocation, values: Float32Array): Void;
|
||||
public static function setVector2(location: ConstantLocation, value: FastVector2): Void;
|
||||
public static function setVector3(location: ConstantLocation, value: FastVector3): Void;
|
||||
public static function setVector4(location: ConstantLocation, value: FastVector4): Void;
|
||||
public static function setMatrix(location: ConstantLocation, value: FastMatrix4): Void;
|
||||
public static function setMatrix3(location: ConstantLocation, value: FastMatrix3): Void;
|
||||
public static function setBuffer(buffer: ShaderStorageBuffer, index: Int): Void;
|
||||
public static function setTexture(unit: TextureUnit, texture: Image, access: Access): Void;
|
||||
public static function setSampledTexture(unit: TextureUnit, texture: Image): Void;
|
||||
public static function setSampledDepthTexture(unit: TextureUnit, texture: Image): Void;
|
||||
public static function setSampledCubeMap(unit: TextureUnit, cubeMap: CubeMap): Void;
|
||||
public static function setSampledDepthCubeMap(unit: TextureUnit, cubeMap: CubeMap): Void;
|
||||
public static function setTextureParameters(unit: TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void;
|
||||
public static function setTexture3DParameters(unit: TextureUnit, uAddressing: TextureAddressing, vAddressing: TextureAddressing,
|
||||
wAddressing: TextureAddressing, minificationFilter: TextureFilter, magnificationFilter: TextureFilter, mipmapFilter: MipMapFilter): Void;
|
||||
public static function setShader(shader: Shader): Void;
|
||||
public static function compute(x: Int, y: Int, z: Int): Void;
|
||||
}
|
||||
3
Kha/Sources/kha/compute/ConstantLocation.hx
Normal file
@ -0,0 +1,3 @@
|
||||
package kha.compute;
|
||||
|
||||
interface ConstantLocation {}
|
||||
10
Kha/Sources/kha/compute/Shader.hx
Normal file
@ -0,0 +1,10 @@
|
||||
package kha.compute;
|
||||
|
||||
import kha.Blob;
|
||||
|
||||
extern class Shader {
|
||||
public function new(sources: Array<Blob>, files: Array<String>);
|
||||
public function delete(): Void;
|
||||
public function getConstantLocation(name: String): ConstantLocation;
|
||||
public function getTextureUnit(name: String): TextureUnit;
|
||||
}
|
||||
12
Kha/Sources/kha/compute/ShaderStorageBuffer.hx
Normal file
@ -0,0 +1,12 @@
|
||||
package kha.compute;
|
||||
|
||||
import kha.graphics4.VertexData;
|
||||
|
||||
extern class ShaderStorageBuffer {
|
||||
public function new(indexCount: Int, type: VertexData);
|
||||
public function delete(): Void;
|
||||
public function lock(): Array<Int>;
|
||||
public function unlock(): Void;
|
||||
public function set(): Void;
|
||||
public function count(): Int;
|
||||
}
|
||||
3
Kha/Sources/kha/compute/TextureUnit.hx
Normal file
@ -0,0 +1,3 @@
|
||||
package kha.compute;
|
||||
|
||||
interface TextureUnit {}
|
||||
@ -8,4 +8,5 @@ enum abstract TextureFormat(Int) to Int {
|
||||
var RGBA64 = 4; // Half floats
|
||||
var A32 = 5; // Float
|
||||
var A16 = 6; // Half float
|
||||
var R32UI = 7; // Unsigned 32-bit integer
|
||||
}
|
||||
|
||||
BIN
Krom/Krom.exe
@ -1,7 +0,0 @@
|
||||
# The zlib/libpng License
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
This notice may not be removed or altered from any source distribution.
|
||||
@ -1,7 +0,0 @@
|
||||
* 2024-12-02: Submodules have been moved in to this repository (previously contained in https://github.com/leenkx3d/lnxsdk).
|
||||
* 2019-06-30: Return value of `PhysicsWorld.rayCast()` changed, see https://github.com/leenkx3d/leenkx/commit/dfb7609a28cebf3a520e6a25a8563d01c32f2b01.
|
||||
* 2019-04-06: Use voxelao instead of voxelgi, gi will be reworked into raytracing.
|
||||
* 2019-01-13: If you are using Leenkx Updater, get Leenkx 0.6beta from https://leenkx.itch.io/leenkx3d first (or clone the sdk from https://github.com/leenkx3d/lnxsdk).
|
||||
* 2018-08-28: `LampObject` and `LampData` is now `LightObject` and `LightData`
|
||||
* 2018-06-01: 'Not Equal' has been removed from the Gate logic node. Use 'Equal' and 'False' output socket instead.
|
||||
* 2017-11-20: Use `leenkx.trait.physics.*` instead of `leenkx.trait.internal.*` to access physics traits.
|
||||
93
leenkx.py
@ -6,12 +6,13 @@ bl_info = {
|
||||
"location": "Properties -> Render -> Leenkx Player",
|
||||
"description": "Full Stack SDK",
|
||||
"author": "Leenkx.com",
|
||||
"version": (2026, 5, 0),
|
||||
"blender": (4, 5, 0),
|
||||
"version": (2026, 10, 6),
|
||||
"blender": (5, 2, 0),
|
||||
"doc_url": "https://leenkx.com/",
|
||||
"tracker_url": "https://leenkx.com/support"
|
||||
}
|
||||
from enum import IntEnum
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
@ -32,6 +33,41 @@ from bpy.app.handlers import persistent
|
||||
from bpy.props import *
|
||||
from bpy.types import Operator, AddonPreferences
|
||||
|
||||
LEENKX_PREFS_ENV = 'LEENKX_ADDON_PREFS'
|
||||
|
||||
persist_props = [
|
||||
'apply_theme', 'show_advanced', 'sdk_path', 'tabs',
|
||||
'code_editor', 'ide_bin', 'ui_scale', 'viewport_controls',
|
||||
'khamake_threads', 'khamake_threads_use_auto', 'compilation_server',
|
||||
'renderdoc_path', 'ffmpeg_path', 'save_on_build', 'open_build_directory',
|
||||
'cmft_use_opencl', 'legacy_shaders', 'relative_paths',
|
||||
'debug_console_auto', 'debug_console_visible_sc',
|
||||
'debug_console_scale_in_sc', 'debug_console_scale_out_sc',
|
||||
'android_sdk_root_path', 'android_open_build_apk_directory',
|
||||
'android_apk_copy_path', 'android_apk_copy_open_directory',
|
||||
'html5_copy_path', 'html5_server_port',
|
||||
'html5_server_log', 'profile_exporter', 'khamake_debug',
|
||||
'haxe_times', 'use_leenkx_py_symlink', 'update_submodules',
|
||||
]
|
||||
|
||||
|
||||
def save_prefs():
|
||||
prefs = LeenkxAddonPreferences.get_prefs()
|
||||
data = {k: getattr(prefs, k) for k in persist_props}
|
||||
os.environ[LEENKX_PREFS_ENV] = json.dumps(data)
|
||||
|
||||
|
||||
def restore_prefs():
|
||||
raw = os.environ.get(LEENKX_PREFS_ENV)
|
||||
if not raw:
|
||||
return
|
||||
data = json.loads(raw)
|
||||
prefs = LeenkxAddonPreferences.get_prefs()
|
||||
prefs.skip_update = True
|
||||
for k, v in data.items():
|
||||
if hasattr(prefs, k):
|
||||
setattr(prefs, k, v)
|
||||
|
||||
|
||||
if bpy.app.version < (2, 90, 0):
|
||||
ListType = List
|
||||
@ -116,7 +152,9 @@ class LeenkxAddonPreferences(AddonPreferences):
|
||||
return
|
||||
self.skip_update = True
|
||||
self.sdk_path = bpy.path.reduce_dirs([bpy.path.abspath(self.sdk_path)])[0] + '/'
|
||||
save_prefs()
|
||||
restart_leenkx(context)
|
||||
update_theme(context)
|
||||
|
||||
def ide_bin_update(self, context):
|
||||
if self.skip_update:
|
||||
@ -311,6 +349,11 @@ class LeenkxAddonPreferences(AddonPreferences):
|
||||
" development. Warning: this will invalidate the installation if the SDK is removed"),
|
||||
update=lambda self, context: update_leenkx_py(get_sdk_path(context)),
|
||||
)
|
||||
apply_theme: BoolProperty(
|
||||
name="Apply Leenkx Theme", default=True,
|
||||
description="Automatically apply the Leenkx Blender theme on addon registration",
|
||||
update=lambda self, context: update_theme(context),
|
||||
)
|
||||
|
||||
def draw(self, context):
|
||||
self.skip_update = False
|
||||
@ -318,7 +361,7 @@ class LeenkxAddonPreferences(AddonPreferences):
|
||||
layout.label(text="Welcome to Leenkx!")
|
||||
|
||||
# Compare version Blender and Leenkx (major, minor)
|
||||
if bpy.app.version[:2] not in [(4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
||||
if bpy.app.version[:2] not in [(5, 2), (4, 5), (4, 4), (4, 2), (3, 6), (3, 3)]:
|
||||
box = layout.box().column()
|
||||
box.label(text="Warning: For Leenkx to work correctly, use a Blender LTS version")
|
||||
|
||||
@ -450,6 +493,7 @@ class LeenkxAddonPreferences(AddonPreferences):
|
||||
|
||||
col = box.column(align=True)
|
||||
col.prop(self, "use_leenkx_py_symlink")
|
||||
col.prop(self, "apply_theme")
|
||||
|
||||
@staticmethod
|
||||
def get_prefs() -> 'LeenkxAddonPreferences':
|
||||
@ -458,9 +502,10 @@ class LeenkxAddonPreferences(AddonPreferences):
|
||||
|
||||
|
||||
def get_fp():
|
||||
if bpy.data.filepath == '':
|
||||
filepath = getattr(bpy.data, 'filepath', '')
|
||||
if filepath == '':
|
||||
return ''
|
||||
s = bpy.data.filepath.split(os.path.sep)
|
||||
s = filepath.split(os.path.sep)
|
||||
s.pop()
|
||||
return os.path.sep.join(s)
|
||||
|
||||
@ -934,13 +979,47 @@ def on_load_post(context):
|
||||
bpy.app.timers.register(lambda: restart_leenkx(bpy.context), first_interval=0.1)
|
||||
|
||||
|
||||
def remove_leenkx_theme():
|
||||
if "leenkx.theme" in sys.modules:
|
||||
sys.modules["leenkx.theme"].unregister()
|
||||
del sys.modules["leenkx.theme"]
|
||||
|
||||
|
||||
def apply_leenkx_theme(sdk_path: str):
|
||||
remove_leenkx_theme()
|
||||
import importlib.util
|
||||
theme_path = os.path.join(sdk_path, "leenkx", "blender", "theme", "theme.py")
|
||||
if os.path.exists(theme_path):
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"leenkx.theme", theme_path)
|
||||
theme = importlib.util.module_from_spec(spec)
|
||||
sys.modules["leenkx.theme"] = theme
|
||||
spec.loader.exec_module(theme)
|
||||
theme.register(sdk_path)
|
||||
else:
|
||||
print("Leenkx theme: theme.py not found at", theme_path)
|
||||
|
||||
|
||||
def update_theme(context):
|
||||
prefs = LeenkxAddonPreferences.get_prefs()
|
||||
if prefs.apply_theme:
|
||||
sdk_path = get_sdk_path(context)
|
||||
if sdk_path != "":
|
||||
apply_leenkx_theme(sdk_path)
|
||||
else:
|
||||
remove_leenkx_theme()
|
||||
|
||||
|
||||
def on_register_post():
|
||||
detect_sdk_path()
|
||||
save_prefs()
|
||||
restart_leenkx(bpy.context)
|
||||
update_theme(bpy.context)
|
||||
|
||||
|
||||
def register():
|
||||
bpy.utils.register_class(LeenkxAddonPreferences)
|
||||
restore_prefs()
|
||||
bpy.utils.register_class(LnxAddonPrintVersionInfoButton)
|
||||
bpy.utils.register_class(LnxAddonInstallButton)
|
||||
bpy.utils.register_class(LnxAddonUpdateButton)
|
||||
@ -953,7 +1032,11 @@ def register():
|
||||
|
||||
|
||||
def unregister():
|
||||
if bpy.app.timers.is_registered(on_register_post):
|
||||
bpy.app.timers.unregister(on_register_post)
|
||||
remove_leenkx_theme()
|
||||
stop_leenkx()
|
||||
save_prefs()
|
||||
bpy.utils.unregister_class(LeenkxAddonPreferences)
|
||||
bpy.utils.unregister_class(LnxAddonInstallButton)
|
||||
bpy.utils.unregister_class(LnxAddonPrintVersionInfoButton)
|
||||
|
||||
17
leenkx/Shaders/add_pass/add_pass.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"contexts": [
|
||||
{
|
||||
"name": "add_pass",
|
||||
"depth_write": false,
|
||||
"compare_mode": "always",
|
||||
"cull_mode": "none",
|
||||
"blend_source": "source_alpha",
|
||||
"blend_destination": "inverse_source_alpha",
|
||||
"blend_operation": "add",
|
||||
"links": [],
|
||||
"texture_params": [],
|
||||
"vertex_shader": "../include/pass.vert.glsl",
|
||||
"fragment_shader": "../include/pass_copy.frag.glsl"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -48,6 +48,10 @@ void main() {
|
||||
}
|
||||
|
||||
if (currentMipLevel == 0) {
|
||||
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
|
||||
fragColor.rgb = vec3(0.0);
|
||||
}
|
||||
|
||||
// https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/#3.2
|
||||
// https://catlikecoding.com/unity/tutorials/advanced-rendering/bloom/#3.4
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
uniform sampler2D tex;
|
||||
uniform sampler2D gbuffer0; // Roughness
|
||||
uniform sampler2D gbufferD; // Depth
|
||||
|
||||
uniform vec2 dirInv;
|
||||
|
||||
@ -14,19 +15,46 @@ out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
float roughness = textureLod(gbuffer0, texCoord, 0.0).b;
|
||||
// if (roughness == 0.0) { // Always blur for now, non blured output can produce noise
|
||||
// fragColor.rgb = textureLod(tex, texCoord).rgb;
|
||||
// return;
|
||||
// }
|
||||
if (roughness >= 0.8) { // No reflections
|
||||
if (roughness >= 0.8) {
|
||||
fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb;
|
||||
return;
|
||||
}
|
||||
|
||||
fragColor.rgb = textureLod(tex, texCoord + dirInv * 2.5, 0.0).rgb;
|
||||
fragColor.rgb += textureLod(tex, texCoord + dirInv * 1.5, 0.0).rgb;
|
||||
fragColor.rgb += textureLod(tex, texCoord, 0.0).rgb;
|
||||
fragColor.rgb += textureLod(tex, texCoord - dirInv * 1.5, 0.0).rgb;
|
||||
fragColor.rgb += textureLod(tex, texCoord - dirInv * 2.5, 0.0).rgb;
|
||||
fragColor.rgb /= vec3(5.0);
|
||||
if (roughness < 0.01) {
|
||||
fragColor.rgb = textureLod(tex, texCoord, 0.0).rgb;
|
||||
return;
|
||||
}
|
||||
|
||||
float blurRadius = 1.0 + roughness * 4.0;
|
||||
vec3 center = textureLod(tex, texCoord, 0.0).rgb;
|
||||
|
||||
float centerDepth = textureLod(gbufferD, texCoord, 0.0).r;
|
||||
|
||||
float w0 = 1.0 / (1.0 + roughness * 2.0);
|
||||
float w1 = 1.0 / (1.0 + roughness);
|
||||
float w2 = 1.0 / (1.0 + roughness * 0.5);
|
||||
float totalW = w0;
|
||||
|
||||
fragColor.rgb = center * w0;
|
||||
|
||||
vec2 offsets[4];
|
||||
offsets[0] = dirInv * blurRadius * 2.5;
|
||||
offsets[1] = dirInv * blurRadius * 1.5;
|
||||
offsets[2] = -dirInv * blurRadius * 1.5;
|
||||
offsets[3] = -dirInv * blurRadius * 2.5;
|
||||
float weights[4];
|
||||
weights[0] = w2;
|
||||
weights[1] = w1;
|
||||
weights[2] = w1;
|
||||
weights[3] = w2;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
vec2 sampleTC = texCoord + offsets[i];
|
||||
float sampleDepth = textureLod(gbufferD, sampleTC, 0.0).r;
|
||||
float depthWeight = exp(-abs(centerDepth - sampleDepth) * 100.0);
|
||||
float w = weights[i] * depthWeight;
|
||||
fragColor.rgb += textureLod(tex, sampleTC, 0.0).rgb * w;
|
||||
totalW += w;
|
||||
}
|
||||
|
||||
fragColor.rgb /= vec3(totalW);
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#endif
|
||||
|
||||
uniform sampler2D tex;
|
||||
|
||||
#ifdef _CDepth
|
||||
uniform sampler2D gbufferD;
|
||||
#endif
|
||||
@ -67,6 +68,8 @@ uniform vec3 PPComp14;
|
||||
uniform vec4 PPComp15;
|
||||
uniform vec4 PPComp16;
|
||||
uniform vec4 PPComp18;
|
||||
uniform vec4 PPComp19;
|
||||
uniform vec4 PPComp20;
|
||||
#endif
|
||||
|
||||
// #ifdef _CPos
|
||||
@ -230,6 +233,45 @@ vec3 lensflare(vec2 uv, vec2 pos) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _CDistort
|
||||
float distortHash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float distortValueNoise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
vec2 u = f * f * (3.0 - 2.0 * f);
|
||||
|
||||
float a = distortHash(i);
|
||||
float b = distortHash(i + vec2(1.0, 0.0));
|
||||
float c = distortHash(i + vec2(0.0, 1.0));
|
||||
float d = distortHash(i + vec2(1.0, 1.0));
|
||||
|
||||
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
||||
}
|
||||
|
||||
vec2 distortSmoothNoise(vec2 p) {
|
||||
return vec2(
|
||||
distortValueNoise(p),
|
||||
distortValueNoise(p + vec2(5.2, 1.3))
|
||||
);
|
||||
}
|
||||
|
||||
vec2 distortUV(vec2 uv, vec2 nUV, float t, float strength) {
|
||||
float intensity = 0.01 * strength;
|
||||
float scale = 4.0;
|
||||
float speed = 0.25;
|
||||
|
||||
nUV.x += t * speed;
|
||||
nUV.y += t * speed;
|
||||
vec2 noise = distortSmoothNoise(nUV * scale);
|
||||
|
||||
uv += (-1.0 + noise * 2.0) * intensity;
|
||||
return uv;
|
||||
}
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
vec2 texCo = texCoord;
|
||||
#ifdef _DynRes
|
||||
@ -252,22 +294,25 @@ void main() {
|
||||
|
||||
#ifdef _CFishEye
|
||||
#ifdef _CPostprocess
|
||||
const float fishEyeStrength = -(PPComp2.y);
|
||||
float fishEyeStrength = PPComp2.y;
|
||||
#else
|
||||
const float fishEyeStrength = -0.01;
|
||||
float fishEyeStrength = compoFisheyeStrength;
|
||||
#endif
|
||||
const vec2 m = vec2(0.5, 0.5);
|
||||
vec2 d = texCo - m;
|
||||
float r = sqrt(dot(d, d));
|
||||
float power = (2.0 * PI / (2.0 * sqrt(dot(m, m)))) * fishEyeStrength;
|
||||
float bind;
|
||||
if (power > 0.0) { bind = sqrt(dot(m, m)); }
|
||||
else { bind = m.x; }
|
||||
if (power > 0.0) {
|
||||
texCo = m + normalize(d) * tan(r * power) * bind / tan(bind * power);
|
||||
}
|
||||
else {
|
||||
texCo = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
|
||||
|
||||
if (abs(fishEyeStrength) > 0.0001) {
|
||||
const vec2 m = vec2(0.5, 0.5);
|
||||
vec2 d = texCo - m;
|
||||
float r = sqrt(dot(d, d));
|
||||
float power = - (2.0 * PI / (2.0 * sqrt(dot(m, m)))) * fishEyeStrength;
|
||||
float bind;
|
||||
if (power > 0.0) { bind = sqrt(dot(m, m)); }
|
||||
else { bind = m.x; }
|
||||
if (power > 0.0) {
|
||||
texCo = m + normalize(d) * tan(r * power) * bind / tan(bind * power);
|
||||
}
|
||||
else {
|
||||
texCo = m + normalize(d) * atan(r * -power * 10.0) * bind / atan(-power * bind * 10.0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -277,9 +322,28 @@ void main() {
|
||||
#else
|
||||
float strengthDistort = compoDistortStrength;
|
||||
#endif
|
||||
float uX = time * strengthDistort;
|
||||
texCo.y = texCo.y + (sin(texCo.x*4.0+uX*2.0)*0.01);
|
||||
texCo.x = texCo.x + (cos(texCo.y*4.0+uX*2.0)*0.01);
|
||||
|
||||
vec2 nUV = texCo;
|
||||
|
||||
texCo = distortUV(texCo, nUV, time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.1, nUV.y + 0.1), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.2, nUV.y + 0.2), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.3, nUV.y + 0.3), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.4, nUV.y + 0.4), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.5, nUV.y + 0.5), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.6, nUV.y + 0.6), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.7, nUV.y + 0.7), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.8, nUV.y + 0.8), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.9, nUV.y + 0.9), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.15, nUV.y + 0.15), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.25, nUV.y + 0.25), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.35, nUV.y + 0.35), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.45, nUV.y + 0.45), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.55, nUV.y + 0.55), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.65, nUV.y + 0.65), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.75, nUV.y + 0.75), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.85, nUV.y + 0.85), time, strengthDistort);
|
||||
texCo = distortUV(texCo, vec2(nUV.x + 0.95, nUV.y + 0.95), time, strengthDistort);
|
||||
#endif
|
||||
|
||||
#ifdef _CDepth
|
||||
@ -343,6 +407,7 @@ void main() {
|
||||
float compoDistance = PPComp3.x;
|
||||
float compoLength = PPComp3.y;
|
||||
float compoStop = PPComp3.z;
|
||||
vec2 focus = vec2(PPComp19.x, PPComp19.y);
|
||||
|
||||
if (PPComp2.z == 1){
|
||||
compoAutoFocus = true;
|
||||
@ -350,9 +415,9 @@ void main() {
|
||||
compoAutoFocus = false;
|
||||
}
|
||||
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, compoAutoFocus, compoDistance, compoLength, compoStop);
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, compoAutoFocus, compoDistance, compoLength, compoStop, focus, PPComp19.z);
|
||||
#else
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, true, compoDOFDistance, compoDOFLength, compoDOFFstop);
|
||||
fragColor.rgb = dof(texCo, depth, tex, gbufferD, texStep, cameraProj, true, compoDOFDistance, compoDOFLength, compoDOFFstop, vec2(0.5, 0.5), 1.0);
|
||||
#endif
|
||||
#else
|
||||
fragColor = textureLod(tex, texCo, 0.0);
|
||||
@ -382,8 +447,10 @@ void main() {
|
||||
vec3 col4 = textureLod(tex, texCo + vec2(texStep.x, texStep.y) * SharpenSize, 0.0).rgb;
|
||||
vec3 colavg = (col1 + col2 + col3 + col4) * 0.25;
|
||||
|
||||
float edgeMagnitude = length(fragColor.rgb - colavg);
|
||||
fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0));
|
||||
float edgeMagnitude = length(fragColor.rgb - colavg);
|
||||
float luma = dot(fragColor.rgb, vec3(0.299, 0.587, 0.114));
|
||||
float sharpenMask = 1.0 - smoothstep(0.5, 0.8, luma);
|
||||
fragColor.rgb = mix(fragColor.rgb, SharpenColor, min(edgeMagnitude * strengthSharpen * 2.0, 1.0) * sharpenMask);
|
||||
#endif
|
||||
|
||||
#ifdef _CFog
|
||||
@ -433,14 +500,15 @@ void main() {
|
||||
|
||||
#ifdef _CExposure
|
||||
#ifdef _CPostprocess
|
||||
fragColor.rgb+=fragColor.rgb*PPComp8.x;
|
||||
fragColor.rgb *= pow(2.0, PPComp8.x);
|
||||
#else
|
||||
fragColor.rgb+= fragColor.rgb*compoExposureStrength;
|
||||
fragColor.rgb *= pow(2.0, compoExposureStrength);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _CPostprocess
|
||||
fragColor.rgb *= ComputeEV(0.0);
|
||||
fragColor.rgb *= pow(2.0, PPComp20.x); // exposure and gamma
|
||||
#endif
|
||||
|
||||
#ifdef _AutoExposure
|
||||
@ -516,14 +584,16 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
|
||||
fragColor.rgb = tonemapAgXFull(fragColor.rgb);
|
||||
} //else { fragColor.rgb = vec3(0,1,0); //ERROR}
|
||||
#endif
|
||||
|
||||
#ifdef _CGamma
|
||||
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / PPComp20.y));
|
||||
#endif
|
||||
#else
|
||||
#ifdef _CToneFilmic
|
||||
fragColor.rgb = tonemapFilmic(fragColor.rgb); // With gamma
|
||||
#endif
|
||||
#ifdef _CToneFilmic2
|
||||
fragColor.rgb = acesFilm(fragColor.rgb);
|
||||
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 1.0));
|
||||
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / 2.2));
|
||||
#endif
|
||||
#ifdef _CToneReinhard
|
||||
fragColor.rgb = tonemapReinhard(fragColor.rgb);
|
||||
@ -549,6 +619,10 @@ fragColor.rgb = min(fragColor.rgb, 65504 * 0.5);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _CGamma
|
||||
fragColor.rgb = pow(fragColor.rgb, vec3(1.0 / compoGammaStrength));
|
||||
#endif
|
||||
|
||||
#ifdef _CBW
|
||||
// fragColor.rgb = vec3(clamp(dot(fragColor.rgb, fragColor.rgb), 0.0, 1.0));
|
||||
fragColor.rgb = vec3((fragColor.r * 0.3 + fragColor.g * 0.59 + fragColor.b * 0.11) / 3.0) * 2.5;
|
||||
|
||||
@ -245,6 +245,11 @@
|
||||
"name": "PPComp18",
|
||||
"link": "_PPComp18",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
},
|
||||
{
|
||||
"name": "PPComp19",
|
||||
"link": "_PPComp19",
|
||||
"ifdef": ["_CPostprocess"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
||||
@ -17,10 +17,12 @@ in vec3 wnormal;
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_1 | || base color (RGB) | occlusion/specular |
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_2 | _gbuffer2 || velocity (XY) | ignore radiance | unused |
|
||||
| GBUF_IDX_2 | _gbuffer2 || velocity (XY) | ignore radiance | tangent angle |
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_EMISSION | _EmissionShaded || emission color (RGB) | unused |
|
||||
+-------------------+-----------------++--------------+--------------+-----------------+--------------------+
|
||||
| GBUF_IDX_REFRACTION | _SSRefraction || packed IOR | transmittance | surfaceDepth | unused |
|
||||
| | _VoxelRefract || (0-1 range) | | | |
|
||||
|
||||
The indices as well as the GBUF_SIZE define are defined in "compiled.inc".
|
||||
*/
|
||||
@ -52,6 +54,10 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _SSRefraction
|
||||
fragColor[GBUF_IDX_REFRACTION] = vec4(ior, opacity, 0.0, 0.0);
|
||||
fragColor[GBUF_IDX_REFRACTION] = vec4(packIOR(ior), opacity, 0.0, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
fragColor[GBUF_IDX_2].a = -1.0;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -7,9 +7,13 @@ out vec4 fragColor[GBUF_SIZE];
|
||||
|
||||
void main() {
|
||||
fragColor[GBUF_IDX_0] = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
#if GBUF_SIZE > 1
|
||||
fragColor[GBUF_IDX_1] = vec4(color, 1.0);
|
||||
#else
|
||||
fragColor[GBUF_IDX_0] = vec4(color, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(0.0);
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(color, 1.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -8,12 +8,10 @@
|
||||
#ifdef _Irr
|
||||
#include "std/shirr.glsl"
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
#include "std/sss.glsl"
|
||||
#endif
|
||||
#ifdef _SSRS
|
||||
#include "std/ssrs.glsl"
|
||||
#endif
|
||||
#include "std/brdf.glsl"
|
||||
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
@ -25,6 +23,9 @@ uniform sampler2D gbuffer1;
|
||||
#ifdef _EmissionShaded
|
||||
uniform sampler2D gbufferEmission;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
uniform sampler2D gbufferCoatNormal;
|
||||
#endif
|
||||
|
||||
#ifdef _VoxelGI
|
||||
uniform sampler2D voxels_diffuse;
|
||||
@ -91,7 +92,7 @@ uniform mat4 invVP;
|
||||
#ifdef _SinglePoint
|
||||
//!uniform sampler2DShadow shadowMapSpot[1];
|
||||
//!uniform sampler2D shadowMapSpotTransparent[1];
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
//!uniform mat4 LWVPSpotArray[1];
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
@ -136,7 +137,7 @@ uniform vec2 cameraPlane;
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
//!uniform mat4 LWVPSpotArray[1];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
@ -199,6 +200,7 @@ uniform vec3 sunCol;
|
||||
uniform sampler2D shadowMapAtlasSunTransparent;
|
||||
#endif
|
||||
#endif
|
||||
//!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades];
|
||||
#else
|
||||
uniform sampler2DShadow shadowMap;
|
||||
#ifdef _ShadowMapTransparent
|
||||
@ -235,6 +237,9 @@ uniform float time;
|
||||
#endif
|
||||
|
||||
#include "std/light.glsl"
|
||||
#ifdef _SSS
|
||||
#include "std/sss.glsl"
|
||||
#endif
|
||||
|
||||
in vec2 texCoord;
|
||||
in vec3 viewRay;
|
||||
@ -254,13 +259,41 @@ void main() {
|
||||
float metallic;
|
||||
uint matid;
|
||||
unpackFloatInt16(g0.a, metallic, matid);
|
||||
#ifdef _ExtBRDF
|
||||
matid = min(matid, uint(MAX_MATERIALS - 1));
|
||||
|
||||
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
||||
vec4 matp0 = vec4(0.0), matp1 = vec4(0.0), matp2 = vec4(0.0), matp3 = vec4(0.0);
|
||||
vec4 matp4 = vec4(0.0), matp5 = vec4(0.0), matp6 = vec4(0.0), matp7 = vec4(0.0);
|
||||
// TODO: coatIOR=1.5, ior=1.45, thinWall=1.0 move to python make files
|
||||
matp1.z = 1.5;
|
||||
matp3.x = 1.45;
|
||||
matp3.y = 1.0;
|
||||
if (matid >= 3u) {
|
||||
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7);
|
||||
}
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatTintCol = vec3(matp1.w, matp2.x, matp2.y);
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
vec3 sheenTintCol = vec3(matp5.z, matp5.w, matp6.x);
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
vec3 sssColorVal = vec3(matp4.w, matp5.x, matp5.y);
|
||||
vec3 sssRadiusBase = vec3(matp4.x, matp4.y, matp4.z);
|
||||
float sssRadiusScalar = max(max(matp4.x, matp4.y), matp4.z) * matp7.x;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec2 occspec = unpackFloat2(g1.a);
|
||||
// re-investigate clamp basecolor to prevent extreme values causing glitches
|
||||
vec3 basecolor = min(g1.rgb, vec3(2.0));
|
||||
vec3 albedo = surfaceAlbedo(basecolor, metallic);
|
||||
vec3 f0 = surfaceF0(basecolor, metallic);
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
f0 = mix(f0, basecolor, vec3(matp6.y, matp6.z, matp6.w));
|
||||
#endif
|
||||
|
||||
#ifdef _VRStereo
|
||||
bool isLeftEye = texCoord.x < 0.5;
|
||||
vec3 eyePos = isLeftEye ? eyeLeft : eyeRight;
|
||||
@ -279,10 +312,26 @@ void main() {
|
||||
#endif
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec4 gCoat = textureLod(gbufferCoatNormal, texCoord, 0.0);
|
||||
vec3 nCoat;
|
||||
nCoat.z = 1.0 - abs(gCoat.x) - abs(gCoat.y);
|
||||
nCoat.xy = nCoat.z >= 0.0 ? gCoat.xy : octahedronWrap(gCoat.xy);
|
||||
nCoat = normalize(nCoat);
|
||||
#endif
|
||||
|
||||
#ifdef _gbuffer2
|
||||
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
#ifdef _gbuffer2
|
||||
vec3 wTangent = decodeTangent(g2.a, n);
|
||||
#else
|
||||
vec3 wTangent = vec3(0.0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef _MicroShadowing
|
||||
occspec.x = mix(1.0, occspec.x, dotNV); // AO Fresnel
|
||||
@ -295,6 +344,44 @@ void main() {
|
||||
vec3 F = f0;
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
float iblSheenWeight = 1.0;
|
||||
float iblCoatWeight = 1.0;
|
||||
float iblLayerWeight = 1.0;
|
||||
vec3 coatTintAbsorb = vec3(1.0);
|
||||
|
||||
#ifdef _Sheen
|
||||
float sheenAlb = sheenIBLAlbedo(matp0.z, matp0.w, dotNV);
|
||||
iblSheenWeight = max(1.0 - sheenAlb *
|
||||
max(max(sheenTintCol.r, sheenTintCol.g), sheenTintCol.b), 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
float dotNVCoat = max(dot(nCoat, v), 0.0);
|
||||
float coatF = coatIBLFresnel(matp1.x, matp1.z, dotNVCoat);
|
||||
iblCoatWeight = max(1.0 - coatF, 0.0);
|
||||
coatTintAbsorb = mix(vec3(1.0), clamp(coatTintCol, 0.0, 1.0),
|
||||
clamp(1.0 / max(dotNVCoat, 0.3) * 0.2, 0.0, 1.0));
|
||||
#endif
|
||||
|
||||
iblLayerWeight = iblSheenWeight * iblCoatWeight;
|
||||
|
||||
brdf_sheenWeight = iblSheenWeight;
|
||||
brdf_coatWeight = iblCoatWeight;
|
||||
brdf_coatTintAbsorb = coatTintAbsorb;
|
||||
#ifdef _Sheen
|
||||
brdf_sheenAlbedo = sheenAlb;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
brdf_coatF0 = (matp1.z - 1.0) / (matp1.z + 1.0);
|
||||
brdf_coatF0 = brdf_coatF0 * brdf_coatF0;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
brdf_transmissionF0 = (matp3.x - 1.0) / (matp3.x + 1.0);
|
||||
brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0;
|
||||
#endif
|
||||
#endif // _ExtBRDF
|
||||
|
||||
#ifndef _VoxelAOvar
|
||||
#ifndef _VoxelGI
|
||||
// Envmap
|
||||
@ -302,9 +389,7 @@ void main() {
|
||||
vec3 envl = shIrradiance(n, shirr);
|
||||
|
||||
#ifdef _gbuffer2
|
||||
if (g2.b < 0.5) {
|
||||
envl = envl;
|
||||
} else {
|
||||
if (g2.b >= 0.5) {
|
||||
envl = vec3(0.0);
|
||||
}
|
||||
#endif
|
||||
@ -317,20 +402,25 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _Rad
|
||||
#ifdef _Anisotropy
|
||||
vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent,
|
||||
matp0.x, roughness);
|
||||
#else
|
||||
vec3 reflectionWorld = reflect(-v, n);
|
||||
#endif
|
||||
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
||||
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
||||
prefilteredColor = min(prefilteredColor, vec3(20.0));
|
||||
#endif
|
||||
|
||||
#ifdef _EnvLDR
|
||||
envl.rgb = pow(envl.rgb, vec3(2.2));
|
||||
envl.rgb = srgbToLinear(envl.rgb);
|
||||
#ifdef _Rad
|
||||
prefilteredColor = pow(prefilteredColor, vec3(2.2));
|
||||
prefilteredColor = srgbToLinear(prefilteredColor);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= albedo;
|
||||
envl.rgb *= diffuseIBL(albedo, roughness, f0, dotNV);
|
||||
|
||||
#ifdef _Brdf
|
||||
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
|
||||
@ -344,6 +434,68 @@ void main() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
envl.rgb *= iblLayerWeight;
|
||||
|
||||
|
||||
#ifdef _Transmission
|
||||
float transF = transmissionIBLFresnel(matp3.x, dotNV);
|
||||
float transmittance = 1.0 - transF;
|
||||
#ifdef _Rad
|
||||
if (matp2.z > 0.0 && transmittance > 0.0) {
|
||||
vec3 refrDir;
|
||||
if (matp3.y > 0.5) {
|
||||
refrDir = reflect(-v, n);
|
||||
} else {
|
||||
refrDir = transmissionIBLDirection(n, v, matp3.x);
|
||||
}
|
||||
float transLod = getMipFromRoughness(matp2.w, envmapNumMipmaps);
|
||||
vec3 transColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(refrDir), transLod).rgb;
|
||||
transColor = min(transColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
transColor = srgbToLinear(transColor);
|
||||
#endif
|
||||
envl.rgb += albedo * matp2.z * transmittance * transColor * dotNV
|
||||
* iblLayerWeight;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
envl.rgb *= coatTintAbsorb;
|
||||
#ifdef _Rad
|
||||
if (coatF > 0.0) {
|
||||
float coatLod = getMipFromRoughness(matp1.y, envmapNumMipmaps);
|
||||
vec3 coatRefl = reflect(-v, nCoat);
|
||||
vec3 coatColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(coatRefl), coatLod).rgb;
|
||||
coatColor = min(coatColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
coatColor = srgbToLinear(coatColor);
|
||||
#endif
|
||||
envl.rgb += coatColor * coatF * iblSheenWeight;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _Sheen
|
||||
#ifdef _Rad
|
||||
if (sheenAlb > 0.0) {
|
||||
float sheenLod = getMipFromRoughness(matp0.w, envmapNumMipmaps);
|
||||
vec3 sheenRefl = reflect(-v, n);
|
||||
vec3 sheenColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(sheenRefl), sheenLod).rgb;
|
||||
sheenColor = min(sheenColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
sheenColor = srgbToLinear(sheenColor);
|
||||
#endif
|
||||
envl.rgb += sheenColor * sheenTintCol * sheenAlb;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif // _ExtBRDF
|
||||
|
||||
envl.rgb *= envmapStrength * occspec.x;
|
||||
|
||||
fragColor.rgb = envl;
|
||||
@ -352,11 +504,30 @@ void main() {
|
||||
|
||||
#ifdef _VoxelGI
|
||||
fragColor.rgb = textureLod(voxels_diffuse, texCoord, 0.0).rgb * voxelgiDiff;
|
||||
if(roughness < 1.0 && occspec.y > 0.0)
|
||||
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * occspec.y * voxelgiRefl;
|
||||
if(roughness < 1.0) {
|
||||
fragColor.rgb += textureLod(voxels_specular, texCoord, 0.0).rgb * F * voxelgiRefl * occspec.y;
|
||||
}
|
||||
#ifdef _Rad
|
||||
vec3 iblReflection = reflect(-v, n);
|
||||
float iblLod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
||||
vec3 iblPrefiltered = textureLod(senvmapRadiance, envMapEquirect(iblReflection), iblLod).rgb;
|
||||
iblPrefiltered = min(iblPrefiltered, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
iblPrefiltered = srgbToLinear(iblPrefiltered);
|
||||
#endif
|
||||
#ifdef _ExtBRDF
|
||||
iblPrefiltered *= iblLayerWeight;
|
||||
iblPrefiltered *= coatTintAbsorb;
|
||||
#endif
|
||||
fragColor.rgb += iblPrefiltered * F * envmapStrength * occspec.x;
|
||||
#else
|
||||
#ifdef _EnvCol
|
||||
fragColor.rgb += backgroundCol * F * envmapStrength * occspec.x;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
#ifdef _VoxelAOvar
|
||||
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb * voxelgiOcc;
|
||||
fragColor.rgb = textureLod(voxels_ao, texCoord, 0.0).rgb;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -379,7 +550,7 @@ void main() {
|
||||
|
||||
#ifdef _SSGI
|
||||
vec3 ssgiColor = textureLod(ssgitex, texCoord, 0.0).rgb;
|
||||
fragColor.rgb += ssgiColor * albedo;
|
||||
fragColor.rgb += ssgiColor * basecolor;
|
||||
#endif
|
||||
|
||||
#ifdef _EmissionShadeless
|
||||
@ -406,10 +577,42 @@ void main() {
|
||||
float sdotVH = max(0.0, dot(v, sh));
|
||||
float sdotNL = max(0.0, dot(n, sunDir));
|
||||
vec3 svisibility = vec3(1.0);
|
||||
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
#ifdef _Anisotropy
|
||||
vec3 sdirect;
|
||||
if (abs(matp0.x) > 0.001 && dot(wTangent, wTangent) > 0.001) {
|
||||
vec3 sbitangent = normalize(cross(n, wTangent));
|
||||
sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
anisotropicBRDF(f0, roughness, matp0.x, matp0.y,
|
||||
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
|
||||
} else {
|
||||
sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
}
|
||||
#else
|
||||
vec3 sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
float sunLayerWeight;
|
||||
sdirect = applyExtBRDFLayers(sdirect, albedo, f0, roughness,
|
||||
sdotNL, dotNV, sdotNH, sdotVH, n, sunDir, v, sh
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, sheenTintCol
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
, sunLayerWeight);
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSunArray[0];
|
||||
#endif
|
||||
#ifdef _CSM
|
||||
svisibility = shadowTestCascade(
|
||||
#ifdef _ShadowMapAtlas
|
||||
@ -494,23 +697,17 @@ void main() {
|
||||
|
||||
fragColor.rgb += sdirect * sunCol * svisibility;
|
||||
|
||||
// #ifdef _Hair // Aniso
|
||||
// if (matid == 2) {
|
||||
// const float shinyParallel = roughness;
|
||||
// const float shinyPerpendicular = 0.1;
|
||||
// const vec3 v = vec3(0.99146, 0.11664, 0.05832);
|
||||
// vec3 T = abs(dot(n, v)) > 0.99999 ? cross(n, vec3(0.0, 1.0, 0.0)) : cross(n, v);
|
||||
// fragColor.rgb = orenNayarDiffuseBRDF(albedo, roughness, dotNV, dotNL, dotVH) + wardSpecular(n, h, dotNL, dotNV, dotNH, T, shinyParallel, shinyPerpendicular) * spec;
|
||||
// }
|
||||
// #endif
|
||||
|
||||
#ifdef _SSS
|
||||
if (matid == 2) {
|
||||
#ifdef _ExtBRDF
|
||||
if (matid >= 3u && matp3.z > 0.0) {
|
||||
#ifdef _CSM
|
||||
int casi, casindex;
|
||||
mat4 LWVP = getCascadeMat(distance(eye, p), casi, casindex);
|
||||
#endif
|
||||
fragColor.rgb += fragColor.rgb * SSSSTransmittance(
|
||||
vec3 sssColor = sssColorVal;
|
||||
float sssRadius = sssRadiusScalar;
|
||||
float sssStrength = matp3.z;
|
||||
vec3 sssResult = SSSSTransmittance(
|
||||
LWVP, p, n, sunDir, lightPlane.y,
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
@ -521,12 +718,26 @@ void main() {
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
);//TODO implement transparent shadowmaps into the SSSSTransmittance()
|
||||
, sssColor, sssRadius
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _CSM
|
||||
, tileBoundsSunArray[casi]
|
||||
#else
|
||||
, tileBoundsSunArray[0]
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
fragColor.rgb += sunCol * sssStrength * sssResult;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // _Sun
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = vec4(0.0, 0.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _SinglePoint
|
||||
|
||||
#ifdef _VRStereo
|
||||
@ -555,12 +766,53 @@ void main() {
|
||||
#ifdef _SSRS
|
||||
, gbufferD, invVP, eye
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, sheenTintCol
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, matp3.z, sssColorVal, sssRadiusBase * matp7.x, matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
|
||||
#ifdef _Spot
|
||||
#ifdef _SSS
|
||||
#ifdef _ShadowMap
|
||||
if (matid == 2) fragColor.rgb += fragColor.rgb * SSSSTransmittance(LWVPSpot[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0]);//TODO implement transparent shadowmaps into the SSSSTransmittance()
|
||||
#ifdef _ExtBRDF
|
||||
if (matid >= 3u && matp3.z > 0.0) {
|
||||
vec3 sssColorSpot = sssColorVal;
|
||||
float sssRadiusSpot = sssRadiusScalar;
|
||||
float sssStrengthSpot = matp3.z;
|
||||
fragColor.rgb += pointCol * sssStrengthSpot * SSSSTransmittance(LWVPSpotArray[0], p, n, normalize(lightPos - p), lightPlane.y, shadowMapSpot[0], sssColorSpot, sssRadiusSpot
|
||||
#ifdef _ShadowMapAtlas
|
||||
, vec4(0.0, 0.0, 1.0, 1.0)
|
||||
#endif
|
||||
);//TODO implement transparent shadowmaps into the SSSSTransmittance()
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef _Spot
|
||||
#ifdef _SSS
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ExtBRDF
|
||||
if (matid >= 3u && matp3.z > 0.0) {
|
||||
vec3 sssColorPoint = sssColorVal;
|
||||
float sssRadiusPoint = sssRadiusScalar;
|
||||
float sssStrengthPoint = matp3.z;
|
||||
fragColor.rgb += pointCol * sssStrengthPoint * SSSSTransmittanceCube(shadowMapPoint[0], lightPos, p, n, normalize(lightPos - p), lightPlane.y, lightProj, sssColorPoint, sssRadiusPoint);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@ -618,7 +870,80 @@ void main() {
|
||||
#ifdef _SSRS
|
||||
, gbufferD, invVP, eye
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, sheenTintCol
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, matp3.z, sssColorVal, sssRadiusBase * matp7.x, matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
|
||||
#ifdef _SSS
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ExtBRDF
|
||||
if (matid >= 3u && matp3.z > 0.0) {
|
||||
vec3 sssColorCL = sssColorVal;
|
||||
float sssRadiusCL = sssRadiusScalar;
|
||||
float sssStrengthCL = matp3.z;
|
||||
vec3 cLightPos = lightsArray[li * 3].xyz;
|
||||
vec3 cLightCol = lightsArray[li * 3 + 1].xyz;
|
||||
vec3 cLightDir = normalize(cLightPos - p);
|
||||
#ifdef _Spot
|
||||
bool isSpotLight = lightsArray[li * 3 + 2].y != 0.0;
|
||||
if (isSpotLight) {
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[li], p, n, cLightDir, lightPlane.y, shadowMapAtlasSpot, sssColorCL, sssRadiusCL, tileBoundsSpotArray[li]);
|
||||
#else
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[li], p, n, cLightDir, lightPlane.y, shadowMapAtlas, sssColorCL, sssRadiusCL, tileBoundsSpotArray[li]);
|
||||
#endif
|
||||
#else
|
||||
if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[0], p, n, cLightDir, lightPlane.y, shadowMapSpot[0], sssColorCL, sssRadiusCL);
|
||||
else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[1], p, n, cLightDir, lightPlane.y, shadowMapSpot[1], sssColorCL, sssRadiusCL);
|
||||
else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[2], p, n, cLightDir, lightPlane.y, shadowMapSpot[2], sssColorCL, sssRadiusCL);
|
||||
else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittance(LWVPSpotArray[3], p, n, cLightDir, lightPlane.y, shadowMapSpot[3], sssColorCL, sssRadiusCL);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlasPoint, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL);
|
||||
#else
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlas, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL);
|
||||
#endif
|
||||
#else
|
||||
if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[0], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[1], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[2], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[3], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlasPoint, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL);
|
||||
#else
|
||||
fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCubeAtlas(shadowMapAtlas, cLightPos, p, n, cLightDir, lightPlane.y, lightProj, li, sssColorCL, sssRadiusCL);
|
||||
#endif
|
||||
#else
|
||||
if (li == 0) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[0], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 1) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[1], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 2) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[2], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
else if (li == 3) fragColor.rgb += cLightCol * sssStrengthCL * SSSSTransmittanceCube(shadowMapPoint[3], cLightPos, p, n, cLightDir, lightPlane.y, lightProj, sssColorCL, sssRadiusCL);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif // _Clusters
|
||||
|
||||
|
||||
@ -138,6 +138,16 @@
|
||||
"link": "_cascadeData",
|
||||
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
|
||||
},
|
||||
{
|
||||
"name": "tileBoundsSunArray",
|
||||
"link": "_tileBoundsSunArray",
|
||||
"ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "tileBoundsSpotArray",
|
||||
"link": "_tileBoundsSpotArray",
|
||||
"ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "lightPlane",
|
||||
"link": "_lightPlane",
|
||||
@ -277,8 +287,15 @@
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats",
|
||||
"ifdef": ["_ExtBRDF"]
|
||||
}
|
||||
],
|
||||
"texture_units": [],
|
||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
||||
"fragment_shader": "deferred_light.frag.glsl",
|
||||
"color_attachments": ["RGBA64"]
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "compiled.inc"
|
||||
#include "std/gbuffer.glsl"
|
||||
#include "std/math.glsl"
|
||||
#include "std/brdf.glsl"
|
||||
#ifdef _Clusters
|
||||
#include "std/clusters.glsl"
|
||||
#endif
|
||||
@ -13,6 +14,12 @@
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
uniform sampler2D gbuffer1;
|
||||
#ifdef _gbuffer2
|
||||
uniform sampler2D gbuffer2;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
uniform sampler2D gbufferCoatNormal;
|
||||
#endif
|
||||
|
||||
uniform float envmapStrength;
|
||||
#ifdef _Irr
|
||||
@ -49,9 +56,15 @@ uniform vec2 cameraPlane;
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
//!uniform sampler2DShadow shadowMapSpot[1];
|
||||
//!uniform mat4 LWVPSpot[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpotArray[1];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform samplerCube shadowMapPointTransparent[1];
|
||||
#endif
|
||||
//!uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
@ -59,26 +72,41 @@ uniform vec2 cameraPlane;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlas;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapAtlasPointTransparent;
|
||||
#endif
|
||||
//!uniform vec4 pointLightDataArray[4];
|
||||
#endif
|
||||
//!uniform vec4 pointLightDataArray[maxLightsCluster * 6];
|
||||
#else
|
||||
//!uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform samplerCube shadowMapPointTransparent[4];
|
||||
#endif
|
||||
#endif
|
||||
//!uniform vec2 lightProj;
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapAtlasSpotTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
//!uniform sampler2DShadow shadowMapSpot[4];
|
||||
//!uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapSpotTransparent[maxLightsCluster];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpotArray[4];
|
||||
#endif
|
||||
//!uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@ -90,9 +118,16 @@ uniform vec3 sunCol;
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSun;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasSunTransparent;
|
||||
#endif
|
||||
#endif
|
||||
//!uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades];
|
||||
#else
|
||||
uniform sampler2DShadow shadowMap;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapTransparent;
|
||||
#endif
|
||||
#endif
|
||||
uniform float shadowsBias;
|
||||
#ifdef _CSM
|
||||
@ -132,17 +167,62 @@ void main() {
|
||||
float metallic;
|
||||
uint matid;
|
||||
unpackFloatInt16(g0.a, metallic, matid);
|
||||
#ifdef _ExtBRDF
|
||||
matid = min(matid, uint(MAX_MATERIALS - 1));
|
||||
|
||||
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
||||
vec4 matp0 = vec4(0.0), matp1 = vec4(0.0), matp2 = vec4(0.0), matp3 = vec4(0.0);
|
||||
vec4 matp4 = vec4(0.0), matp5 = vec4(0.0), matp6 = vec4(0.0), matp7 = vec4(0.0);
|
||||
// TODO: coatIOR=1.5, ior=1.45, thinWall=1.0 move to python make files
|
||||
matp1.z = 1.5;
|
||||
matp3.x = 1.45;
|
||||
matp3.y = 1.0;
|
||||
if (matid >= 3u) {
|
||||
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7);
|
||||
}
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatTintCol = vec3(matp1.w, matp2.x, matp2.y);
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
vec3 sheenTintCol = vec3(matp5.z, matp5.w, matp6.x);
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
vec3 sssColorVal = vec3(matp4.w, matp5.x, matp5.y);
|
||||
vec3 sssRadiusScaled = vec3(matp4.x, matp4.y, matp4.z) * matp7.x;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0); // Basecolor.rgb, spec/occ
|
||||
vec2 occspec = unpackFloat2(g1.a);
|
||||
vec3 albedo = surfaceAlbedo(g1.rgb, metallic); // g1.rgb - basecolor
|
||||
vec3 f0 = surfaceF0(g1.rgb, metallic);
|
||||
vec3 basecolor = min(g1.rgb, vec3(2.0));
|
||||
vec3 albedo = surfaceAlbedo(basecolor, metallic);
|
||||
vec3 f0 = surfaceF0(basecolor, metallic);
|
||||
#ifdef _ExtBRDF
|
||||
f0 = mix(f0, basecolor, vec3(matp6.y, matp6.z, matp6.w));
|
||||
#endif
|
||||
|
||||
float depth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||
vec3 p = getPos(eye, eyeLook, normalize(viewRay), depth, cameraProj);
|
||||
vec3 v = normalize(eye - p);
|
||||
float dotNV = max(dot(n, v), 0.0);
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec4 gCoat = textureLod(gbufferCoatNormal, texCoord, 0.0);
|
||||
vec3 nCoat;
|
||||
nCoat.z = 1.0 - abs(gCoat.x) - abs(gCoat.y);
|
||||
nCoat.xy = nCoat.z >= 0.0 ? gCoat.xy : octahedronWrap(gCoat.xy);
|
||||
nCoat = normalize(nCoat);
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
#ifdef _gbuffer2
|
||||
vec4 g2 = textureLod(gbuffer2, texCoord, 0.0);
|
||||
vec3 wTangent = decodeTangent(g2.a, n);
|
||||
#else
|
||||
vec3 wTangent = vec3(0.0);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _Brdf
|
||||
vec2 envBRDF = texelFetch(senvmapBrdf, ivec2(vec2(dotNV, 1.0 - roughness) * 256.0), 0).xy;
|
||||
#endif
|
||||
@ -154,32 +234,144 @@ void main() {
|
||||
envl /= PI;
|
||||
#endif
|
||||
#else
|
||||
vec3 envl = vec3(1.0);
|
||||
vec3 envl = vec3(0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Rad
|
||||
#ifdef _Anisotropy
|
||||
vec3 reflectionWorld = anisotropicIBLDirection(n, v, wTangent,
|
||||
matp0.x, roughness);
|
||||
#else
|
||||
vec3 reflectionWorld = reflect(-v, n);
|
||||
#endif
|
||||
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
||||
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef _EnvLDR
|
||||
envl.rgb = pow(envl.rgb, vec3(2.2));
|
||||
envl.rgb = srgbToLinear(envl.rgb);
|
||||
#ifdef _Rad
|
||||
prefilteredColor = pow(prefilteredColor, vec3(2.2));
|
||||
prefilteredColor = srgbToLinear(prefilteredColor);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= albedo;
|
||||
#ifdef _Brdf
|
||||
vec3 F = f0 * envBRDF.x + envBRDF.y;
|
||||
#else
|
||||
vec3 F = f0;
|
||||
#endif
|
||||
|
||||
envl.rgb *= diffuseIBL(albedo, roughness, f0, dotNV);
|
||||
|
||||
#ifdef _Brdf
|
||||
envl.rgb *= 1.0 - F;
|
||||
#endif
|
||||
|
||||
#ifdef _Rad // Indirect specular
|
||||
envl.rgb += prefilteredColor * (f0 * envBRDF.x + envBRDF.y) * 1.5 * occspec.y;
|
||||
envl.rgb += prefilteredColor * F;
|
||||
#else
|
||||
#ifdef _EnvCol
|
||||
envl.rgb += backgroundCol * surfaceF0(g1.rgb, metallic); // f0
|
||||
envl.rgb += backgroundCol * F;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
float iblSheenWeight = 1.0;
|
||||
float iblCoatWeight = 1.0;
|
||||
vec3 coatTintAbsorb = vec3(1.0);
|
||||
|
||||
#ifdef _Sheen
|
||||
float sheenAlb = sheenIBLAlbedo(matp0.z, matp0.w, dotNV);
|
||||
iblSheenWeight = max(1.0 - sheenAlb *
|
||||
max(max(sheenTintCol.r, sheenTintCol.g), sheenTintCol.b), 0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
float dotNVCoat = max(dot(nCoat, v), 0.0);
|
||||
float coatF = coatIBLFresnel(matp1.x, matp1.z, dotNVCoat);
|
||||
iblCoatWeight = max(1.0 - coatF, 0.0);
|
||||
if (matp1.x > 0.0) {
|
||||
coatTintAbsorb = mix(vec3(1.0), clamp(coatTintCol, 0.0, 1.0),
|
||||
clamp(1.0 / max(dotNVCoat, 0.3) * 0.2, 0.0, 1.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
float iblLayerWeight = iblSheenWeight * iblCoatWeight;
|
||||
envl.rgb *= iblLayerWeight;
|
||||
|
||||
brdf_sheenWeight = iblSheenWeight;
|
||||
brdf_coatWeight = iblCoatWeight;
|
||||
brdf_coatTintAbsorb = coatTintAbsorb;
|
||||
#ifdef _Sheen
|
||||
brdf_sheenAlbedo = sheenAlb;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
brdf_coatF0 = (matp1.z - 1.0) / (matp1.z + 1.0);
|
||||
brdf_coatF0 = brdf_coatF0 * brdf_coatF0;
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
brdf_transmissionF0 = (matp3.x - 1.0) / (matp3.x + 1.0);
|
||||
brdf_transmissionF0 = brdf_transmissionF0 * brdf_transmissionF0;
|
||||
#endif
|
||||
|
||||
#ifdef _Transmission
|
||||
float transF = transmissionIBLFresnel(matp3.x, dotNV);
|
||||
float transmittance = 1.0 - transF;
|
||||
#ifdef _Rad
|
||||
if (matp2.z > 0.0 && transmittance > 0.0) {
|
||||
vec3 refrDir;
|
||||
if (matp3.y > 0.5) {
|
||||
refrDir = reflect(-v, n);
|
||||
} else {
|
||||
refrDir = transmissionIBLDirection(n, v, matp3.x);
|
||||
}
|
||||
float transLod = getMipFromRoughness(matp2.w, envmapNumMipmaps);
|
||||
vec3 transColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(refrDir), transLod).rgb;
|
||||
transColor = min(transColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
transColor = srgbToLinear(transColor);
|
||||
#endif
|
||||
envl.rgb += albedo * matp2.z * transmittance * transColor * dotNV
|
||||
* iblLayerWeight;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
envl.rgb *= coatTintAbsorb;
|
||||
#ifdef _Rad
|
||||
if (coatF > 0.0) {
|
||||
float coatLod = getMipFromRoughness(matp1.y, envmapNumMipmaps);
|
||||
vec3 coatRefl = reflect(-v, nCoat);
|
||||
vec3 coatColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(coatRefl), coatLod).rgb;
|
||||
coatColor = min(coatColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
coatColor = srgbToLinear(coatColor);
|
||||
#endif
|
||||
envl.rgb += coatColor * coatF * iblSheenWeight;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _Sheen
|
||||
#ifdef _Rad
|
||||
if (sheenAlb > 0.0) {
|
||||
float sheenLod = getMipFromRoughness(matp0.w, envmapNumMipmaps);
|
||||
vec3 sheenRefl = reflect(-v, n);
|
||||
vec3 sheenColor = textureLod(senvmapRadiance,
|
||||
envMapEquirect(sheenRefl), sheenLod).rgb;
|
||||
sheenColor = min(sheenColor, vec3(20.0));
|
||||
#ifdef _EnvLDR
|
||||
sheenColor = srgbToLinear(sheenColor);
|
||||
#endif
|
||||
envl.rgb += sheenColor * sheenTintCol * sheenAlb;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif // _ExtBRDF
|
||||
|
||||
envl.rgb *= envmapStrength * occspec.x;
|
||||
fragColor.rgb = envl;
|
||||
|
||||
@ -188,37 +380,107 @@ void main() {
|
||||
float sdotNH = max(0.0, dot(n, sh));
|
||||
float sdotVH = max(0.0, dot(v, sh));
|
||||
float sdotNL = max(0.0, dot(n, sunDir));
|
||||
float svisibility = 1.0;
|
||||
vec3 sdirect = lambertDiffuseBRDF(albedo, sdotNL) +
|
||||
vec3 svisibility = vec3(1.0);
|
||||
#ifdef _Anisotropy
|
||||
vec3 sdirect;
|
||||
if (abs(matp0.x) > 0.001 && dot(wTangent, wTangent) > 0.001) {
|
||||
vec3 sbitangent = normalize(cross(n, wTangent));
|
||||
sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
anisotropicBRDF(f0, roughness, matp0.x, matp0.y,
|
||||
wTangent, sbitangent, n, sunDir, v, sdotNL, dotNV) * occspec.y;
|
||||
} else {
|
||||
sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
}
|
||||
#else
|
||||
vec3 sdirect = diffuseBRDF(albedo, roughness, f0, sdotNL, dotNV, sdotVH) +
|
||||
specularBRDF(f0, roughness, sdotNL, sdotNH, dotNV, sdotVH) * occspec.y;
|
||||
#endif
|
||||
|
||||
float sunSheenWeight = brdf_sheenWeight;
|
||||
float sunCoatWeight = brdf_coatWeight;
|
||||
|
||||
#ifdef _Sheen
|
||||
vec3 sunSheen = sheenBRDF(matp0.z, matp0.w, sheenTintCol, sdotNL, sdotNH, dotNV);
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
vec3 sunCoat = clearcoatBRDF(matp1.x, matp1.y, matp1.z, nCoat, sunDir, v, sh);
|
||||
#endif
|
||||
|
||||
float sunLayerWeight = sunSheenWeight * sunCoatWeight;
|
||||
sdirect *= sunLayerWeight;
|
||||
#ifdef _Transmission
|
||||
sdirect += transmissionBRDF(albedo, matp2.z, matp2.w, matp3.x, matp3.y, sdotNL, dotNV, sdotVH) * sunLayerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
sdirect *= brdf_coatTintAbsorb;
|
||||
sdirect += sunCoat * sunSheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
sdirect += sunSheen;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMap
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSunArray[0];
|
||||
#endif
|
||||
#ifdef _CSM
|
||||
svisibility = shadowTestCascade(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
#else
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMap, shadowMapTransparent
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
#endif
|
||||
, eye, p + n * shadowsBias * 2, shadowsBias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, false
|
||||
#endif
|
||||
, eye, p + n * shadowsBias * 10, shadowsBias
|
||||
);
|
||||
#else
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 100, 1.0);
|
||||
vec4 lPos = LWVP * vec4(p + n * shadowsBias * 2, 1.0);
|
||||
if (lPos.w > 0.0) svisibility = shadowTest(
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun, shadowMapAtlasSunTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
#else
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSun
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
shadowMap
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMap, shadowMapTransparent
|
||||
#else
|
||||
shadowMap
|
||||
#endif
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, shadowsBias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, false
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
@ -226,15 +488,37 @@ void main() {
|
||||
fragColor.rgb += sdirect * svisibility * sunCol;
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = vec4(0.0, 0.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _SinglePoint
|
||||
fragColor.rgb += sampleLight(
|
||||
p, n, v, dotNV, pointPos, pointCol, albedo, roughness, occspec.y, f0
|
||||
#ifdef _ShadowMap
|
||||
, 0, pointBias, true
|
||||
#ifdef _ShadowMapTransparent
|
||||
, false
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, true, spotData.x, spotData.y, spotDir, spotData.zw, spotRight // TODO: Test!
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, sheenTintCol
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, matp3.z, sssColorVal, sssRadiusScaled, matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
|
||||
@ -268,16 +552,41 @@ void main() {
|
||||
#ifdef _ShadowMap
|
||||
// light index, shadow bias, cast_shadows
|
||||
, li, lightsArray[li * 3 + 2].x, lightsArray[li * 3 + 2].z != 0.0
|
||||
#ifdef _ShadowMapTransparent
|
||||
, false
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, lightsArray[li * 3 + 2].y != 0.0
|
||||
, lightsArray[li * 3 + 2].y // spot size (cutoff)
|
||||
, lightsArraySpot[li].w // spot blend (exponent)
|
||||
, lightsArraySpot[li].xyz // spotDir
|
||||
, lightsArraySpot[li * 2].w // spot blend (exponent)
|
||||
, lightsArraySpot[li * 2].xyz // spotDir
|
||||
, vec2(lightsArray[li * 3].w, lightsArray[li * 3 + 1].w) // scale
|
||||
, lightsArraySpot[li * 2 + 1].xyz // right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, matp1.x, matp1.y, matp1.z, coatTintCol, nCoat
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, matp0.z, matp0.w, sheenTintCol
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, matp0.x, matp0.y, wTangent
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, matp3.z, sssColorVal, sssRadiusScaled, matp3.w
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, matp2.z, matp2.w, matp3.x, matp3.y
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif // _Clusters
|
||||
|
||||
fragColor.rgb = clamp(fragColor.rgb, vec3(0.0), vec3(65504.0));
|
||||
if (any(isnan(fragColor.rgb)) || any(isinf(fragColor.rgb))) {
|
||||
fragColor.rgb = vec3(0.0);
|
||||
}
|
||||
|
||||
fragColor.a = 1.0; // Mark as opaque
|
||||
}
|
||||
|
||||
@ -97,6 +97,16 @@
|
||||
"link": "_cascadeData",
|
||||
"ifdef": ["_Sun", "_ShadowMap", "_CSM"]
|
||||
},
|
||||
{
|
||||
"name": "tileBoundsSunArray",
|
||||
"link": "_tileBoundsSunArray",
|
||||
"ifdef": ["_Sun", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "tileBoundsSpotArray",
|
||||
"link": "_tileBoundsSpotArray",
|
||||
"ifdef": ["_Clusters", "_Spot", "_ShadowMap", "_ShadowMapAtlas"]
|
||||
},
|
||||
{
|
||||
"name": "eyeLookRight",
|
||||
"link": "_eyeLookRight",
|
||||
@ -112,11 +122,6 @@
|
||||
"link": "_inverseViewProjectionMatrixRight",
|
||||
"ifdef": ["_VRStereo"]
|
||||
},
|
||||
{
|
||||
"name": "invVP",
|
||||
"link": "_viewProjectionMatrix",
|
||||
"ifdef": ["_SSRS"]
|
||||
},
|
||||
{
|
||||
"name": "smSizeUniform",
|
||||
"link": "_shadowMapSize",
|
||||
@ -127,14 +132,6 @@
|
||||
"link": "_lightPlane",
|
||||
"ifdef": ["_SSS"]
|
||||
},
|
||||
{
|
||||
"name": "VP",
|
||||
"link": "_viewProjectionMatrix",
|
||||
"ifdef": ["_SSRS"]
|
||||
},
|
||||
{
|
||||
"ifdef": ["_SMSizeUniform"]
|
||||
},
|
||||
{
|
||||
"name": "lightProj",
|
||||
"link": "_lightPlaneProj",
|
||||
@ -214,6 +211,12 @@
|
||||
"link": "_biasLightWorldViewProjectionMatrixSpot3",
|
||||
"ifndef": ["_ShadowMapAtlas"],
|
||||
"ifdef": ["_LTC", "_ShadowMap"]
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats",
|
||||
"ifdef": ["_ExtBRDF"]
|
||||
}
|
||||
],
|
||||
"vertex_shader": "../include/pass_viewray.vert.glsl",
|
||||
|
||||
8
leenkx/Shaders/render_draw/render_line.frag.glsl
Normal file
@ -0,0 +1,8 @@
|
||||
#version 450
|
||||
|
||||
in vec4 color;
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
fragColor = vec4(color);
|
||||
}
|
||||
12
leenkx/Shaders/render_draw/render_line.vert.glsl
Normal file
@ -0,0 +1,12 @@
|
||||
#version 450
|
||||
|
||||
in vec3 pos;
|
||||
in vec4 col;
|
||||
|
||||
uniform mat4 ViewProjection;
|
||||
out vec4 color;
|
||||
|
||||
void main() {
|
||||
color = col;
|
||||
gl_Position = ViewProjection * vec4(pos, 1.0);
|
||||
}
|
||||
15
leenkx/Shaders/render_draw/render_line_deferred.frag.glsl
Normal file
@ -0,0 +1,15 @@
|
||||
#version 450
|
||||
|
||||
#include "compiled.inc"
|
||||
|
||||
in vec4 color;
|
||||
out vec4 fragColor[GBUF_SIZE];
|
||||
|
||||
void main() {
|
||||
fragColor[GBUF_IDX_0] = vec4(1.0, 1.0, 0.0, 1.0);
|
||||
fragColor[GBUF_IDX_1] = vec4(color);
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
fragColor[GBUF_IDX_EMISSION] = vec4(color);
|
||||
#endif
|
||||
}
|
||||
@ -28,10 +28,12 @@ in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
const float GOLDEN_ANGLE = 2.39996323;
|
||||
const int RAY_STEPS = 12;
|
||||
const int RAY_STEPS = 6;
|
||||
const int BINARY_STEPS = 3;
|
||||
|
||||
vec2 getProjectedCoord(const vec3 viewPos) {
|
||||
vec4 projectedCoord = P * vec4(viewPos, 1.0);
|
||||
if (projectedCoord.w <= 0.0) return vec2(-1e5);
|
||||
projectedCoord.xy /= projectedCoord.w;
|
||||
projectedCoord.xy = projectedCoord.xy * 0.5 + 0.5;
|
||||
#ifdef _InvY
|
||||
@ -40,16 +42,12 @@ vec2 getProjectedCoord(const vec3 viewPos) {
|
||||
return projectedCoord.xy;
|
||||
}
|
||||
|
||||
vec3 cosineSampleHemisphere(vec3 n, vec2 rand) {
|
||||
float phi = PI * 2.0 * rand.x;
|
||||
float cosTheta = sqrt(1.0 - rand.y);
|
||||
float sinTheta = sqrt(rand.y);
|
||||
|
||||
vec3 h = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
|
||||
|
||||
vec3 tangent, bitangent;
|
||||
float linearZ(const float depth) {
|
||||
return -P[3].z / (depth + P[2].z);
|
||||
}
|
||||
|
||||
void buildTBN(vec3 n, out vec3 tangent, out vec3 bitangent) {
|
||||
vec3 absN = abs(n);
|
||||
|
||||
if (absN.x <= absN.y && absN.x <= absN.z) {
|
||||
tangent = normalize(cross(n, vec3(1.0, 0.0, 0.0)));
|
||||
} else if (absN.y <= absN.z) {
|
||||
@ -58,46 +56,70 @@ vec3 cosineSampleHemisphere(vec3 n, vec2 rand) {
|
||||
tangent = normalize(cross(n, vec3(0.0, 0.0, 1.0)));
|
||||
}
|
||||
bitangent = cross(n, tangent);
|
||||
|
||||
}
|
||||
|
||||
vec3 sampleHemisphere(vec3 n, vec3 tangent, vec3 bitangent, float phi, float cosTheta) {
|
||||
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
|
||||
vec3 h = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
|
||||
return normalize(tangent * h.x + bitangent * h.y + n * h.z);
|
||||
}
|
||||
|
||||
vec3 traceRay(vec3 origin, vec3 dir, float maxDist, float minDist) {
|
||||
vec3 traceRay(vec3 origin, vec3 dir, float maxDist, float minDist, float jitter) {
|
||||
float stepSize = maxDist / float(RAY_STEPS);
|
||||
vec3 pos = origin + dir * minDist;
|
||||
float rayDist = minDist + stepSize * (jitter - 1.0);
|
||||
vec3 pos = origin + dir * rayDist;
|
||||
|
||||
float prevDepthDiff = 0.0;
|
||||
float hadValidPrev = 0.0;
|
||||
bool hasPrev = false;
|
||||
|
||||
for (int i = 1; i <= RAY_STEPS; i++) {
|
||||
for (int i = 0; i < RAY_STEPS; i++) {
|
||||
pos += dir * stepSize;
|
||||
rayDist += stepSize;
|
||||
vec2 uv = getProjectedCoord(pos);
|
||||
if (uv.x < -100.0) return vec3(-1.0);
|
||||
uv = clamp(uv, vec2(0.001), vec2(0.999));
|
||||
|
||||
vec2 sampleUV = clamp(uv, vec2(0.001), vec2(0.999));
|
||||
|
||||
float sampleDepth = textureLod(gbufferD, sampleUV, 0.0).r * 2.0 - 1.0;
|
||||
float sampleDepth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
|
||||
if (sampleDepth == 1.0) {
|
||||
hadValidPrev = 0.0;
|
||||
hasPrev = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
float depthDiff = pos.z - linearZ(sampleDepth);
|
||||
float thickness = maxDist * 0.075 + rayDist * 0.125;
|
||||
|
||||
vec3 sampleViewPos = getPosView2(invP, sampleDepth, sampleUV);
|
||||
float depthDiff = pos.z - sampleViewPos.z;
|
||||
float rayDist = length(pos - origin);
|
||||
float thickness = 0.15 + rayDist * 0.25;
|
||||
bool crossed = hasPrev && (prevDepthDiff > 0.0) && (depthDiff <= 0.0);
|
||||
bool withinThickness = (depthDiff <= 0.0) && (-depthDiff < thickness);
|
||||
|
||||
float crossed = hadValidPrev * step(0.0, prevDepthDiff) * step(depthDiff, 0.0);
|
||||
float withinThickness = step(abs(depthDiff), thickness);
|
||||
|
||||
if (crossed > 0.5 || withinThickness > 0.5) {
|
||||
float distWeight = 1.0 - (rayDist / maxDist);
|
||||
distWeight = max(0.0, distWeight * distWeight);
|
||||
if (crossed || withinThickness) {
|
||||
vec3 bPos = pos;
|
||||
vec3 bDir = dir * stepSize;
|
||||
for (int j = 0; j < BINARY_STEPS; j++) {
|
||||
bDir *= 0.5;
|
||||
bPos -= bDir;
|
||||
vec2 bUV = getProjectedCoord(bPos);
|
||||
bUV = clamp(bUV, vec2(0.001), vec2(0.999));
|
||||
float bDepth = textureLod(gbufferD, bUV, 0.0).r * 2.0 - 1.0;
|
||||
if (bDepth == 1.0) {
|
||||
bPos += bDir;
|
||||
continue;
|
||||
}
|
||||
if (bPos.z - linearZ(bDepth) > 0.0) bPos += bDir;
|
||||
}
|
||||
|
||||
return vec3(sampleUV, distWeight);
|
||||
vec2 bestUV = getProjectedCoord(bPos);
|
||||
if (bestUV.x < -100.0) return vec3(-1.0);
|
||||
bestUV = clamp(bestUV, vec2(0.001), vec2(0.999));
|
||||
|
||||
float hitDist = length(bPos - origin);
|
||||
float distWeight = max(0.0, 1.0 - (hitDist / maxDist));
|
||||
distWeight *= distWeight;
|
||||
|
||||
return vec3(bestUV, distWeight);
|
||||
}
|
||||
|
||||
prevDepthDiff = depthDiff;
|
||||
hadValidPrev = 1.0;
|
||||
hasPrev = true;
|
||||
}
|
||||
|
||||
return vec3(-1.0);
|
||||
@ -111,77 +133,77 @@ void main() {
|
||||
}
|
||||
|
||||
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||
vec2 enc = g0.rg;
|
||||
vec3 n;
|
||||
n.z = 1.0 - abs(enc.x) - abs(enc.y);
|
||||
n.xy = n.z >= 0.0 ? enc.xy : octahedronWrap(enc.xy);
|
||||
n = normalize(n);
|
||||
vec3 n = getNor(g0.rg);
|
||||
vec3 basecolor = textureLod(gbuffer1, texCoord, 0.0).rgb;
|
||||
|
||||
vec3 viewNormal = V3 * n;
|
||||
vec3 viewPos = getPosView2(invP, depth, texCoord);
|
||||
|
||||
#ifdef _CPostprocess
|
||||
float radius = PPComp12.y;
|
||||
float strength = PPComp12.x;
|
||||
float radius = PPComp12.y * 2.0;
|
||||
float strength = PPComp12.x * 0.5;
|
||||
#else
|
||||
float radius = ssgiRadius;
|
||||
float strength = ssgiStrength;
|
||||
float radius = ssgiRadius * 2.0;
|
||||
float strength = ssgiStrength * 0.5;
|
||||
#endif
|
||||
|
||||
float noise = fract(52.9829189 * fract(0.06711056 * texCoord.x * 1000.0 + 0.00583715 * texCoord.y * 1000.0));
|
||||
radius = min(radius, max(-viewPos.z / P[1].y, 0.05));
|
||||
|
||||
float noise = fract(52.9829189 * fract(0.06711056 * gl_FragCoord.x + 0.00583715 * gl_FragCoord.y));
|
||||
|
||||
vec3 gi = vec3(0.0);
|
||||
int validSamples = 0;
|
||||
float missWeight = 0.0;
|
||||
|
||||
// min distance to avoid self shadowing artiffacts
|
||||
float minDist = radius * 0.05;
|
||||
|
||||
vec3 tangent, bitangent;
|
||||
buildTBN(viewNormal, tangent, bitangent);
|
||||
|
||||
for (int i = 0; i < ssgiSamples; i++) {
|
||||
float fi = float(i) + noise;
|
||||
vec2 rand = vec2(
|
||||
fract(fi * 0.7548776662 + noise),
|
||||
fract(fi * 0.5698402909 + noise * 1.5)
|
||||
);
|
||||
float phi = float(i) * GOLDEN_ANGLE + noise * PI2;
|
||||
float cosTheta = sqrt(max(0.0, 1.0 - (float(i) + 0.5) / float(ssgiSamples)));
|
||||
float jitter = fract(noise + float(i) * 0.618034);
|
||||
|
||||
vec3 rayDir = cosineSampleHemisphere(viewNormal, rand);
|
||||
vec3 hitResult = traceRay(viewPos, rayDir, radius, minDist);
|
||||
vec3 rayDir = sampleHemisphere(viewNormal, tangent, bitangent, phi, cosTheta);
|
||||
vec3 hitResult = traceRay(viewPos, rayDir, radius, minDist, jitter);
|
||||
|
||||
if (hitResult.x < 0.0) continue;
|
||||
if (hitResult.x < 0.0) {
|
||||
missWeight += 1.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
vec2 hitUV = hitResult.xy;
|
||||
float distWeight = hitResult.z;
|
||||
|
||||
vec3 hitAlbedo = textureLod(gbuffer1, hitUV, 1.0).rgb;
|
||||
vec3 hitN = getNor(textureLod(gbuffer0, hitUV, 0.0).rg);
|
||||
float emitterCos = max(0.0, dot(V3 * hitN, -rayDir));
|
||||
if (emitterCos <= 0.0) {
|
||||
missWeight += 1.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
vec3 hitAlbedo = textureLod(gbuffer1, hitUV, 0.0).rgb;
|
||||
|
||||
#ifdef _Sun
|
||||
vec4 hitG0 = textureLod(gbuffer0, hitUV, 0.0);
|
||||
vec2 hitEnc = hitG0.rg;
|
||||
vec3 hitN;
|
||||
hitN.z = 1.0 - abs(hitEnc.x) - abs(hitEnc.y);
|
||||
hitN.xy = hitN.z >= 0.0 ? hitEnc.xy : octahedronWrap(hitEnc.xy);
|
||||
hitN = normalize(hitN);
|
||||
float hitNdotL = max(0.0, dot(hitN, sunDir));
|
||||
vec3 hitRadiance = hitAlbedo * sunCol * hitNdotL;
|
||||
vec3 hitRadiance = hitAlbedo * (sunCol * hitNdotL + vec3(0.4));
|
||||
#else
|
||||
vec3 hitRadiance = hitAlbedo * 0.5;
|
||||
vec3 hitRadiance = hitAlbedo * 0.4;
|
||||
#endif
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
hitRadiance += textureLod(gbufferEmission, hitUV, 0.0).rgb;
|
||||
#endif
|
||||
|
||||
gi += hitRadiance * distWeight;
|
||||
validSamples++;
|
||||
gi += hitRadiance * (distWeight * emitterCos);
|
||||
}
|
||||
|
||||
if (validSamples > 0) {
|
||||
gi /= float(validSamples);
|
||||
}
|
||||
|
||||
gi *= strength;
|
||||
gi += basecolor * 0.1 * missWeight;
|
||||
gi *= 2.0 * strength / float(ssgiSamples);
|
||||
|
||||
#ifdef _EmissionShaded
|
||||
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb * 0.3;
|
||||
gi += textureLod(gbufferEmission, texCoord, 0.0).rgb * 0.3;
|
||||
#endif
|
||||
|
||||
fragColor = vec4(min(gi, vec3(2.0)), 1.0);
|
||||
|
||||
@ -11,6 +11,7 @@ uniform sampler2D gbuffer1; // basecol, spec
|
||||
uniform mat4 P;
|
||||
uniform mat3 V3;
|
||||
uniform vec2 cameraProj;
|
||||
uniform vec2 screenSize;
|
||||
|
||||
#ifdef _CPostprocess
|
||||
uniform vec3 PPComp9;
|
||||
@ -24,8 +25,8 @@ out vec4 fragColor;
|
||||
vec3 hitCoord;
|
||||
float depth;
|
||||
|
||||
const int numBinarySearchSteps = 7;
|
||||
const int maxSteps = int(ceil(1.0 / ssrRayStep) * ssrSearchDist);
|
||||
const int numBinarySearchSteps = 8;
|
||||
const int maxSteps = 50;
|
||||
|
||||
vec2 getProjectedCoord(const vec3 hit) {
|
||||
vec4 projectedCoord = P * vec4(hit, 1.0);
|
||||
@ -38,44 +39,58 @@ vec2 getProjectedCoord(const vec3 hit) {
|
||||
}
|
||||
|
||||
float getDeltaDepth(const vec3 hit) {
|
||||
depth = textureLod(gbufferD, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0;
|
||||
vec2 tc = getProjectedCoord(hit);
|
||||
if (tc.x < 0.0 || tc.x > 1.0 || tc.y < 0.0 || tc.y > 1.0)
|
||||
return -1.0;
|
||||
depth = textureLod(gbufferD, tc, 0.0).r * 2.0 - 1.0;
|
||||
vec3 viewPos = getPosView(viewRay, depth, cameraProj);
|
||||
return viewPos.z - hit.z;
|
||||
}
|
||||
|
||||
vec4 binarySearch(vec3 dir) {
|
||||
vec4 binarySearch(vec3 dir, float stepSize) {
|
||||
float ddepth;
|
||||
for (int i = 0; i < numBinarySearchSteps; i++) {
|
||||
dir *= 0.5;
|
||||
hitCoord -= dir;
|
||||
stepSize *= 0.5;
|
||||
hitCoord -= dir * stepSize;
|
||||
ddepth = getDeltaDepth(hitCoord);
|
||||
if (ddepth < 0.0) hitCoord += dir;
|
||||
if (ddepth < 0.0) hitCoord += dir * stepSize;
|
||||
}
|
||||
// Ugly discard of hits too far away
|
||||
#ifdef _CPostprocess
|
||||
if (abs(ddepth) > PPComp9.z / 500) return vec4(0.0);
|
||||
float maxDist = PPComp9.z;
|
||||
#else
|
||||
if (abs(ddepth) > ssrSearchDist / 500) return vec4(0.0);
|
||||
float maxDist = ssrSearchDist;
|
||||
#endif
|
||||
return vec4(getProjectedCoord(hitCoord), 0.0, 1.0);
|
||||
if (abs(ddepth) > maxDist * 0.005) return vec4(0.0);
|
||||
vec2 hitTC = getProjectedCoord(hitCoord);
|
||||
if (hitTC.x < 0.0 || hitTC.x > 1.0 || hitTC.y < 0.0 || hitTC.y > 1.0)
|
||||
return vec4(0.0);
|
||||
return vec4(hitTC, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec4 rayCast(vec3 dir) {
|
||||
#ifdef _CPostprocess
|
||||
dir *= PPComp9.x;
|
||||
float baseStep = PPComp9.x;
|
||||
float maxDist = PPComp9.z;
|
||||
#else
|
||||
dir *= ssrRayStep;
|
||||
float baseStep = ssrRayStep;
|
||||
float maxDist = ssrSearchDist;
|
||||
#endif
|
||||
float stepSize = baseStep * max(1.0, -viewRay.z * 0.1);
|
||||
vec3 startPos = hitCoord;
|
||||
for (int i = 0; i < maxSteps; i++) {
|
||||
hitCoord += dir;
|
||||
if (getDeltaDepth(hitCoord) > 0.0) return binarySearch(dir);
|
||||
hitCoord += dir * stepSize;
|
||||
float dist = length(hitCoord - startPos);
|
||||
if (dist > maxDist) break;
|
||||
float ddepth = getDeltaDepth(hitCoord);
|
||||
if (ddepth > 0.0) return binarySearch(dir, stepSize);
|
||||
stepSize *= 1.03;
|
||||
}
|
||||
return vec4(0.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||
float roughness = unpackFloat(g0.b).y;
|
||||
float roughness = g0.b;
|
||||
if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; }
|
||||
|
||||
float spec = fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
||||
@ -92,30 +107,54 @@ void main() {
|
||||
|
||||
vec3 viewNormal = V3 * n;
|
||||
vec3 viewPos = getPosView(viewRay, d, cameraProj);
|
||||
vec3 reflected = reflect(viewPos, viewNormal);
|
||||
float NdotV = clamp(dot(viewNormal, -normalize(viewPos)), 0.0, 1.0);
|
||||
vec3 reflected = reflect(normalize(viewPos), viewNormal);
|
||||
hitCoord = viewPos;
|
||||
|
||||
#ifdef _CPostprocess
|
||||
vec3 dir = reflected * (1.0 - rand(texCoord) * PPComp10.y * roughness) * 2.0;
|
||||
#else
|
||||
vec3 dir = reflected * (1.0 - rand(texCoord) * ssrJitter * roughness) * 2.0;
|
||||
#endif
|
||||
vec3 dir = reflected;
|
||||
|
||||
// * max(ssrMinRayStep, -viewPos.z)
|
||||
vec4 coords = rayCast(dir);
|
||||
|
||||
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
||||
float screenEdgeFactor = clamp(1.0 - (deltaCoords.x + deltaCoords.y), 0.0, 1.0);
|
||||
if (coords.w <= 0.0) {
|
||||
fragColor.rgb = vec3(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
||||
float screenEdgeFactor = smoothstep(0.5, 0.15, deltaCoords.x)
|
||||
* smoothstep(0.5, 0.15, deltaCoords.y);
|
||||
screenEdgeFactor = max(screenEdgeFactor, 0.15);
|
||||
|
||||
float hitDepth = textureLod(gbufferD, coords.xy, 0.0).r * 2.0 - 1.0;
|
||||
vec3 hitViewPos = getPosView(viewRay, hitDepth, cameraProj);
|
||||
vec3 hitDir = normalize(hitViewPos - viewPos);
|
||||
float hitNdotV = clamp(dot(viewNormal, -hitDir), 0.0, 1.0);
|
||||
float hitBackFace = smoothstep(-0.15, 0.3, hitNdotV);
|
||||
|
||||
float reflectivity = 1.0 - roughness;
|
||||
#ifdef _CPostprocess
|
||||
float intensity = pow(reflectivity, PPComp10.x) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * clamp((PPComp9.z - length(viewPos - hitCoord)) * (1.0 / PPComp9.z), 0.0, 1.0) * coords.w;
|
||||
float falloffExp = PPComp10.x;
|
||||
float maxDist = PPComp9.z;
|
||||
#else
|
||||
float intensity = pow(reflectivity, ssrFalloffExp) * screenEdgeFactor * clamp(-reflected.z, 0.0, 1.0) * clamp((ssrSearchDist - length(viewPos - hitCoord)) * (1.0 / ssrSearchDist), 0.0, 1.0) * coords.w;
|
||||
float falloffExp = ssrFalloffExp;
|
||||
float maxDist = ssrSearchDist;
|
||||
#endif
|
||||
|
||||
float distAttenuation = 1.0 - clamp(length(viewPos - hitCoord) / maxDist, 0.0, 1.0);
|
||||
distAttenuation = pow(distAttenuation, 1.5);
|
||||
|
||||
float fresnel = pow(1.0 - NdotV, 5.0);
|
||||
fresnel = mix(0.04, 1.0, fresnel);
|
||||
|
||||
float intensity = pow(reflectivity, falloffExp) * screenEdgeFactor
|
||||
* smoothstep(0.0, 0.1, -reflected.z)
|
||||
* distAttenuation
|
||||
* hitBackFace
|
||||
* coords.w;
|
||||
|
||||
intensity = clamp(intensity, 0.0, 1.0);
|
||||
|
||||
vec3 reflCol = textureLod(tex, coords.xy, 0.0).rgb;
|
||||
reflCol = clamp(reflCol, 0.0, 1.0);
|
||||
fragColor.rgb = reflCol * intensity * 0.5;
|
||||
fragColor.rgb = reflCol * intensity * mix(0.5, 1.0, fresnel);
|
||||
}
|
||||
|
||||
@ -22,6 +22,10 @@
|
||||
"name": "cameraProj",
|
||||
"link": "_cameraPlaneProj"
|
||||
},
|
||||
{
|
||||
"name": "screenSize",
|
||||
"link": "_screenSize"
|
||||
},
|
||||
{
|
||||
"name": "PPComp9",
|
||||
"link": "_PPComp9",
|
||||
|
||||
@ -12,6 +12,7 @@ uniform sampler2D tex1;
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
uniform sampler2D gbufferD1;
|
||||
uniform sampler2D gbuffer1;
|
||||
|
||||
uniform sampler2D gbuffer_refraction; // ior\opacity
|
||||
uniform mat4 P;
|
||||
@ -26,7 +27,7 @@ vec3 hitCoord;
|
||||
float depth;
|
||||
|
||||
const int numBinarySearchSteps = 7;
|
||||
const int maxSteps = int(ceil(1.0 / ss_refractionRayStep) * ss_refractionSearchDist);
|
||||
const int maxSteps = 50;
|
||||
|
||||
vec2 getProjectedCoord(const vec3 hit) {
|
||||
vec4 projectedCoord = P * vec4(hit, 1.0);
|
||||
@ -39,45 +40,60 @@ vec2 getProjectedCoord(const vec3 hit) {
|
||||
}
|
||||
|
||||
float getDeltaDepth(const vec3 hit) {
|
||||
depth = textureLod(gbufferD1, getProjectedCoord(hit), 0.0).r * 2.0 - 1.0;
|
||||
vec2 tc = getProjectedCoord(hit);
|
||||
if (tc.x < 0.0 || tc.x > 1.0 || tc.y < 0.0 || tc.y > 1.0)
|
||||
return -1.0;
|
||||
depth = textureLod(gbufferD1, tc, 0.0).r * 2.0 - 1.0;
|
||||
vec3 viewPos = getPosView(viewRay, depth, cameraProj);
|
||||
return viewPos.z - hit.z;
|
||||
}
|
||||
|
||||
vec4 binarySearch(vec3 dir) {
|
||||
vec4 binarySearch(vec3 dir, float stepSize) {
|
||||
float ddepth;
|
||||
for (int i = 0; i < numBinarySearchSteps; i++) {
|
||||
dir *= 0.5;
|
||||
hitCoord -= dir;
|
||||
stepSize *= 0.5;
|
||||
hitCoord -= dir * stepSize;
|
||||
ddepth = getDeltaDepth(hitCoord);
|
||||
if (ddepth < 0.0) hitCoord += dir;
|
||||
if (ddepth < 0.0) hitCoord += dir * stepSize;
|
||||
}
|
||||
if (abs(ddepth) > ss_refractionSearchDist) return vec4(0.0);
|
||||
return vec4(getProjectedCoord(hitCoord), 0.0, 1.0);
|
||||
if (abs(ddepth) > ss_refractionSearchDist * 0.005) return vec4(0.0);
|
||||
vec2 hitTC = getProjectedCoord(hitCoord);
|
||||
if (hitTC.x < 0.0 || hitTC.x > 1.0 || hitTC.y < 0.0 || hitTC.y > 1.0)
|
||||
return vec4(0.0);
|
||||
return vec4(hitTC, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec4 rayCast(vec3 dir) {
|
||||
float ddepth;
|
||||
dir *= ss_refractionRayStep;
|
||||
float stepSize = ss_refractionRayStep * max(1.0, -viewRay.z * 0.1);
|
||||
vec3 startPos = hitCoord;
|
||||
for (int i = 0; i < maxSteps; i++) {
|
||||
hitCoord += dir;
|
||||
ddepth = getDeltaDepth(hitCoord);
|
||||
if (ddepth > 0.0) return binarySearch(dir);
|
||||
hitCoord += dir * stepSize;
|
||||
float dist = length(hitCoord - startPos);
|
||||
if (dist > ss_refractionSearchDist) break;
|
||||
float ddepth = getDeltaDepth(hitCoord);
|
||||
if (ddepth > 0.0) return binarySearch(dir, stepSize);
|
||||
stepSize *= 1.03;
|
||||
}
|
||||
return vec4(texCoord, 0.0, 0.0);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 gr = textureLod(gbuffer_refraction, texCoord, 0.0);
|
||||
float ior = gr.x;
|
||||
float ior = unpackIOR(gr.x);
|
||||
float transmittance = gr.y;
|
||||
float surfaceDepth = gr.z;
|
||||
float surfaceDepth = textureLod(gbufferD, texCoord, 0.0).r;
|
||||
float d = surfaceDepth * 2.0 - 1.0;
|
||||
|
||||
vec4 sceneSample = textureLod(tex, texCoord, 0.0);
|
||||
if (surfaceDepth == 0.0 || transmittance == 0.0 || ior == 1.0) {
|
||||
if (gr.z == 0.0 || surfaceDepth == 1.0) {
|
||||
fragColor = sceneSample;
|
||||
return;
|
||||
}
|
||||
|
||||
vec4 g1 = textureLod(gbuffer1, texCoord, 0.0);
|
||||
if (transmittance == 0.0 || ior == 1.0) {
|
||||
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
|
||||
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
|
||||
fragColor.rgb = g1.rgb + background * transmittance;
|
||||
fragColor.a = 1.0;
|
||||
return;
|
||||
}
|
||||
@ -96,18 +112,18 @@ void main() {
|
||||
vec3 refracted = refract(incident, viewNormal, 1.0 / ior);
|
||||
if (length(refracted) < 0.001) {
|
||||
vec3 background = textureLod(tex1, texCoord, 0.0).rgb;
|
||||
fragColor.rgb = sceneSample.rgb + background * (1.0 - sceneSample.a);
|
||||
fragColor.rgb = g1.rgb + background * transmittance;
|
||||
fragColor.a = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
hitCoord = viewPos;
|
||||
|
||||
vec3 dir = refracted * (1.0 - rand(texCoord) * ss_refractionJitter * roughness) * 2.0;
|
||||
vec3 dir = normalize(refracted);
|
||||
vec4 coords = rayCast(dir);
|
||||
|
||||
vec2 screenEdge = smoothstep(0.0, 0.1, coords.xy) * smoothstep(0.0, 0.1, 1.0 - coords.xy);
|
||||
float screenEdgeFactor = screenEdge.x * screenEdge.y;
|
||||
vec2 screenEdge = smoothstep(0.0, 0.05, coords.xy) * smoothstep(0.0, 0.05, 1.0 - coords.xy);
|
||||
float screenEdgeFactor = max(screenEdge.x * screenEdge.y, 0.05);
|
||||
float refractivity = 1.0 - roughness;
|
||||
|
||||
float intensity = pow(refractivity, ss_refractionFalloffExp) * screenEdgeFactor * coords.w;
|
||||
@ -118,6 +134,6 @@ void main() {
|
||||
|
||||
vec3 behindColor = mix(straightBackground, refractedBackground, intensity);
|
||||
|
||||
fragColor.rgb = sceneSample.rgb + behindColor * (1.0 - sceneSample.a);
|
||||
fragColor.rgb = g1.rgb + behindColor * transmittance;
|
||||
fragColor.a = 1.0;
|
||||
}
|
||||
|
||||
@ -40,100 +40,148 @@
|
||||
|
||||
uniform sampler2D gbufferD;
|
||||
uniform sampler2D gbuffer0;
|
||||
uniform sampler2D gbuffer1;
|
||||
uniform sampler2D tex;
|
||||
|
||||
uniform vec2 dir;
|
||||
uniform vec2 cameraProj;
|
||||
uniform mat4 projectionMatrix;
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
//!uniform vec4 materialParams[MAX_MATERIALS * 8];
|
||||
#endif
|
||||
|
||||
in vec2 texCoord;
|
||||
out vec4 fragColor;
|
||||
|
||||
const vec3 SKIN_SSS_RADIUS = vec3(4.8, 2.4, 1.5);
|
||||
const float SSS_DISTANCE_SCALE = 0.001;
|
||||
// TODO: finish the SSS
|
||||
const float SSS_SCALE = 0.05;
|
||||
const float DEPTH_THRESHOLD = 0.05;
|
||||
|
||||
// Temp hash func -
|
||||
float hash13(vec3 p3) {
|
||||
p3 = fract(p3 * vec3(0.1031, 0.1030, 0.0973));
|
||||
p3 += dot(p3, p3.yzx + 33.33);
|
||||
return fract((p3.x + p3.y) * p3.z);
|
||||
}
|
||||
|
||||
vec4 SSSSBlur() {
|
||||
const int SSSS_N_SAMPLES = 15;
|
||||
vec4 SSSSBlur(vec3 sssRadius, float sssWeight) {
|
||||
const int SSSS_N_SAMPLES = 11;
|
||||
vec4 kernel[SSSS_N_SAMPLES];
|
||||
|
||||
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample
|
||||
kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37mm
|
||||
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97mm
|
||||
kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93mm
|
||||
kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87mm
|
||||
kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53mm (red only)
|
||||
kernel[6] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||
kernel[7] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||
kernel[8] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37mm
|
||||
kernel[9] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97mm
|
||||
kernel[10] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93mm
|
||||
kernel[11] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87mm
|
||||
kernel[12] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53mm (red only)
|
||||
kernel[13] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||
kernel[14] = vec4(0.0, 0.0, 0.0, 0.0); // Unused
|
||||
|
||||
vec4 colorM = textureLod(tex, texCoord, 0.0);
|
||||
|
||||
float depth = textureLod(gbufferD, texCoord, 0.0).r;
|
||||
kernel[0] = vec4(0.233, 0.455, 0.649, 0.0); // Center sample
|
||||
kernel[1] = vec4(0.100, 0.336, 0.344, 0.37); // +0.37
|
||||
kernel[2] = vec4(0.118, 0.198, 0.0, 0.97); // +0.97
|
||||
kernel[3] = vec4(0.113, 0.007, 0.007, 1.93); // +1.93
|
||||
kernel[4] = vec4(0.358, 0.004, 0.0, 3.87); // +3.87
|
||||
kernel[5] = vec4(0.078, 0.0, 0.0, 6.53); // +6.53 (red only)
|
||||
kernel[6] = vec4(0.100, 0.336, 0.344, -0.37); // -0.37
|
||||
kernel[7] = vec4(0.118, 0.198, 0.0, -0.97); // -0.97
|
||||
kernel[8] = vec4(0.113, 0.007, 0.007, -1.93); // -1.93
|
||||
kernel[9] = vec4(0.358, 0.004, 0.0, -3.87); // -3.87
|
||||
kernel[10] = vec4(0.078, 0.0, 0.0, -6.53); // -6.53 (red only)
|
||||
|
||||
vec2 texSize = vec2(textureSize(tex, 0));
|
||||
ivec2 texelCoord = ivec2(texCoord * texSize);
|
||||
|
||||
vec4 colorM = texelFetch(tex, texelCoord, 0);
|
||||
vec3 albedo = texelFetch(gbuffer1, texelCoord, 0).rgb;
|
||||
|
||||
vec3 irradianceM = colorM.rgb / max(albedo, vec3(0.00001));
|
||||
|
||||
float depth = texelFetch(gbufferD, texelCoord, 0).r;
|
||||
float depthM = cameraProj.y / (depth - cameraProj.x);
|
||||
|
||||
float distanceScale = 1.0 / max(depthM, 0.1);
|
||||
|
||||
vec2 finalStep = sssWidth * distanceScale * dir * SSS_DISTANCE_SCALE;
|
||||
|
||||
float blurWidth = max(max(sssRadius.r, sssRadius.g), sssRadius.b);
|
||||
float projScale = dot(dir, vec2(projectionMatrix[0][0], projectionMatrix[1][1]));
|
||||
vec2 finalStep = blurWidth * (1.0 / depthM) * dir * projScale * SSS_SCALE;
|
||||
|
||||
vec3 jitterSeed = vec3(texCoord.xy * 1000.0, fract(cameraProj.x * 0.0001));
|
||||
float jitterOffset = (hash13(jitterSeed) * 2.0 - 1.0) * 0.15;
|
||||
|
||||
finalStep *= (1.0 + jitterOffset);
|
||||
vec3 colorBlurred = vec3(0.0);
|
||||
vec3 weightSum = vec3(0.0);
|
||||
colorBlurred += colorM.rgb * kernel[0].rgb;
|
||||
weightSum += kernel[0].rgb;
|
||||
|
||||
|
||||
vec3 colorBlurred = irradianceM * kernel[0].rgb;
|
||||
vec3 weightSum = kernel[0].rgb;
|
||||
|
||||
for (int i = 1; i < SSSS_N_SAMPLES; i++) {
|
||||
float sampleJitter = hash13(vec3(texCoord.xy * 720.0, float(i) * 37.45)) * 0.1 - 0.05;
|
||||
vec2 offset = texCoord + (kernel[i].a + sampleJitter) * finalStep;
|
||||
vec4 color = textureLod(tex, offset, 0.0);
|
||||
const float DEPTH_THRESHOLD = 0.05;
|
||||
float sampleDepth = textureLod(gbufferD, offset, 0.0).r;
|
||||
float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
|
||||
|
||||
float depthDiff = abs(depthM - sampleDepthM);
|
||||
float depthWeight = exp(-depthDiff * 10.0);
|
||||
|
||||
if (depthDiff > DEPTH_THRESHOLD) {
|
||||
color.rgb = mix(colorM.rgb, color.rgb, depthWeight);
|
||||
|
||||
vec3 irradiance = irradianceM;
|
||||
float s = 0.0;
|
||||
if (all(greaterThanEqual(offset, vec2(0.0))) && all(lessThan(offset, vec2(1.0)))) {
|
||||
ivec2 sampleTexel = ivec2(offset * texSize);
|
||||
|
||||
float sampleDepth = texelFetch(gbufferD, sampleTexel, 0).r;
|
||||
float sampleDepthM = cameraProj.y / (sampleDepth - cameraProj.x);
|
||||
float depthDiff = abs(depthM - sampleDepthM);
|
||||
|
||||
if (depthDiff < 1.0) {
|
||||
vec4 sampleG0 = texelFetch(gbuffer0, sampleTexel, 0);
|
||||
float sampleMetallic;
|
||||
uint sampleMatid;
|
||||
unpackFloatInt16(sampleG0.a, sampleMetallic, sampleMatid);
|
||||
bool sampleIsSSS = false;
|
||||
#ifdef _ExtBRDF
|
||||
if (sampleMatid >= 3u && sampleMatid < uint(MAX_MATERIALS)) {
|
||||
if (materialParams[sampleMatid * 8u + 3u].z > 0.0) sampleIsSSS = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sampleIsSSS) {
|
||||
vec3 sampleColor = texelFetch(tex, sampleTexel, 0).rgb;
|
||||
vec3 sampleAlbedo = texelFetch(gbuffer1, sampleTexel, 0).rgb;
|
||||
irradiance = sampleColor / max(sampleAlbedo, vec3(0.00001));
|
||||
}
|
||||
|
||||
if (depthDiff <= DEPTH_THRESHOLD) {
|
||||
s = 1.0;
|
||||
} else {
|
||||
s = exp(-depthDiff * 10.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
colorBlurred += color.rgb * kernel[i].rgb;
|
||||
|
||||
colorBlurred += kernel[i].rgb * mix(irradianceM, irradiance, s);
|
||||
weightSum += kernel[i].rgb;
|
||||
}
|
||||
vec3 normalizedColor = colorBlurred / max(weightSum, vec3(0.00001));
|
||||
|
||||
vec3 normalizedIrradiance = colorBlurred / max(weightSum, vec3(0.00001));
|
||||
float dither = hash13(vec3(texCoord * 1333.0, 0.0)) * 0.003 - 0.0015;
|
||||
normalizedColor = max(normalizedColor + vec3(dither), vec3(0.0));
|
||||
return vec4(normalizedColor, colorM.a);
|
||||
normalizedIrradiance = max(normalizedIrradiance + vec3(dither), vec3(0.0));
|
||||
vec3 blurredColor = normalizedIrradiance * albedo;
|
||||
vec3 result = mix(colorM.rgb, blurredColor, sssWeight);
|
||||
return vec4(result, colorM.a);
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 g0 = textureLod(gbuffer0, texCoord, 0.0);
|
||||
vec2 texSize0 = vec2(textureSize(gbuffer0, 0));
|
||||
ivec2 texelCoord0 = ivec2(texCoord * texSize0);
|
||||
vec4 g0 = texelFetch(gbuffer0, texelCoord0, 0);
|
||||
float metallic;
|
||||
uint matid;
|
||||
unpackFloatInt16(g0.a, metallic, matid);
|
||||
|
||||
if (matid == 2u) {
|
||||
vec4 originalColor = textureLod(tex, texCoord, 0.0);
|
||||
vec4 blurredColor = SSSSBlur();
|
||||
vec4 sssContribution = blurredColor - originalColor;
|
||||
vec4 combined = originalColor + max(vec4(0.0), sssContribution) * 0.8;
|
||||
fragColor = max(vec4(0.0), min(combined, vec4(10.0)));
|
||||
bool applySSS = false;
|
||||
vec3 sssRadius = vec3(1.0);
|
||||
float sssWeight = 1.0;
|
||||
vec4 matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7;
|
||||
#ifdef _ExtBRDF
|
||||
if (matid >= 3u && matid < uint(MAX_MATERIALS)) {
|
||||
getMaterialParams(matid, matp0, matp1, matp2, matp3, matp4, matp5, matp6, matp7);
|
||||
// matp3.z = subsurface, matp4.xyz = subsurfaceRadiusRGB, matp7.x = subsurfaceScale
|
||||
if (matp3.z > 0.0) {
|
||||
applySSS = true;
|
||||
sssRadius = matp4.xyz * matp7.x;
|
||||
sssWeight = matp3.z;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (applySSS) {
|
||||
fragColor = SSSSBlur(sssRadius, sssWeight);
|
||||
} else {
|
||||
fragColor = textureLod(tex, texCoord, 0.0);
|
||||
vec2 texSizeMain = vec2(textureSize(tex, 0));
|
||||
ivec2 texelCoordMain = ivec2(texCoord * texSizeMain);
|
||||
fragColor = texelFetch(tex, texelCoordMain, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,6 +13,16 @@
|
||||
{
|
||||
"name": "cameraProj",
|
||||
"link": "_cameraPlaneProj"
|
||||
},
|
||||
{
|
||||
"name": "projectionMatrix",
|
||||
"link": "_projectionMatrix"
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats",
|
||||
"ifdef": ["_ExtBRDF"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
@ -32,6 +42,16 @@
|
||||
{
|
||||
"name": "cameraProj",
|
||||
"link": "_cameraPlaneProj"
|
||||
},
|
||||
{
|
||||
"name": "projectionMatrix",
|
||||
"link": "_projectionMatrix"
|
||||
},
|
||||
{
|
||||
"name": "materialParams",
|
||||
"link": "_materialParams",
|
||||
"type": "floats",
|
||||
"ifdef": ["_ExtBRDF"]
|
||||
}
|
||||
],
|
||||
"texture_params": [],
|
||||
|
||||
@ -1,10 +1,30 @@
|
||||
#ifndef _BRDF_GLSL_
|
||||
#define _BRDF_GLSL_
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535
|
||||
#endif
|
||||
#ifndef INV_PI
|
||||
#define INV_PI 0.3183098861
|
||||
#endif
|
||||
#ifndef INV_TWO_PI
|
||||
#define INV_TWO_PI 0.1591549430
|
||||
#endif
|
||||
#ifndef SCHLICK_A
|
||||
#define SCHLICK_A -5.55473
|
||||
#endif
|
||||
#ifndef SCHLICK_B
|
||||
#define SCHLICK_B -6.98316
|
||||
#endif
|
||||
#ifndef SRGB_GAMMA
|
||||
#define SRGB_GAMMA 2.2
|
||||
#endif
|
||||
#define srgbToLinear(x) pow(x, vec3(SRGB_GAMMA))
|
||||
|
||||
// http://xlgames-inc.github.io/posts/improvedibl/
|
||||
// http://blog.selfshadow.com/publications/s2013-shading-course/
|
||||
vec3 f_schlick(const vec3 f0, const float vh) {
|
||||
return f0 + (1.0 - f0) * exp2((-5.55473 * vh - 6.98316) * vh);
|
||||
return f0 + (1.0 - f0) * exp2((SCHLICK_A * vh + SCHLICK_B) * vh);
|
||||
}
|
||||
|
||||
float v_smithschlick(const float nl, const float nv, const float a) {
|
||||
@ -31,7 +51,7 @@ float d_ggx(const float nh, const float a) {
|
||||
float a2 = a * a;
|
||||
float denom = nh * nh * (a2 - 1.0) + 1.0;
|
||||
denom = max(denom * denom, 0.00006103515625 /* 2^-14 = smallest possible half float value, prevent div by zero */);
|
||||
return a2 * (1.0 / 3.1415926535) / denom;
|
||||
return a2 * INV_PI / denom;
|
||||
}
|
||||
|
||||
vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const float nh, const float nv, const float vh) {
|
||||
@ -44,11 +64,10 @@ vec3 specularBRDF(const vec3 f0, const float roughness, const float nl, const fl
|
||||
// http://filmicworlds.com/blog/optimizing-ggx-shaders-with-dotlh/
|
||||
vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, const float dotNH, const float dotLH) {
|
||||
// D
|
||||
const float pi = 3.1415926535;
|
||||
float alpha = roughness * roughness;
|
||||
float alphaSqr = alpha * alpha;
|
||||
float denom = dotNH * dotNH * (alphaSqr - 1.0) + 1.0;
|
||||
float D = alphaSqr / (pi * denom * denom);
|
||||
float D = alphaSqr / (PI * denom * denom);
|
||||
// F
|
||||
const float F_a = 1.0;
|
||||
float F_b = pow(1.0 - dotLH, 5.0);
|
||||
@ -65,21 +84,201 @@ vec3 specularBRDFb(const vec3 f0, const float roughness, const float dotNL, cons
|
||||
return specular / 4.0; // TODO: get rid of / 4.0
|
||||
}
|
||||
|
||||
vec3 orenNayarDiffuseBRDF(const vec3 albedo, const float roughness, const float nv, const float nl, const float vh) {
|
||||
float a = roughness * roughness;
|
||||
float s = a;
|
||||
float s2 = s * s;
|
||||
float vl = 2.0 * vh * vh - 1.0; // Double angle identity
|
||||
float Cosri = vl - nv * nl;
|
||||
float C1 = 1.0 - 0.5 * s2 / (s2 + 0.33);
|
||||
float test = 1.0;
|
||||
if (Cosri >= 0.0) test = (1.0 / (max(nl, nv)));
|
||||
float C2 = 0.45 * s2 / (s2 + 0.09) * Cosri * test;
|
||||
return albedo * max(0.0, nl) * (C1 + C2) * (1.0 + roughness * 0.5);
|
||||
vec3 lambertDiffuseBRDF(const vec3 albedo, const float nl) {
|
||||
return albedo * INV_PI * nl;
|
||||
}
|
||||
|
||||
vec3 lambertDiffuseBRDF(const vec3 albedo, const float nl) {
|
||||
return albedo * (1.0 / 3.1415926535) * nl;
|
||||
#ifdef _BurleyDiffuse
|
||||
vec3 burleyDiffuseBRDF(const vec3 albedo, const float roughness,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
float nl = clamp(dotNL, 0.0, 1.0);
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float energyBias = mix(0.0, 0.5, roughness);
|
||||
float energyFactor = mix(1.0, 1.0 / 1.51, roughness);
|
||||
float fd90 = energyBias + 2.0 * roughness * dotVH * dotVH;
|
||||
float lightScatter = 1.0 + (fd90 - 1.0) * pow(1.0 - nl, 5.0);
|
||||
float viewScatter = 1.0 + (fd90 - 1.0) * pow(1.0 - nv, 5.0);
|
||||
return albedo * INV_PI * lightScatter * viewScatter * energyFactor * nl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _EONDiffuse
|
||||
const float constant1_FON = 0.5 - 2.0 / (3.0 * PI);
|
||||
const float constant2_FON = 2.0 / 3.0 - 28.0 / (15.0 * PI);
|
||||
|
||||
float E_FON_approx(float mu, float r) {
|
||||
float mucomp = 1.0 - mu;
|
||||
const float g1 = 0.0571085289;
|
||||
const float g2 = 0.491881867;
|
||||
const float g3 = -0.332181442;
|
||||
const float g4 = 0.0714429953;
|
||||
float GoverPi = mucomp * (g1 + mucomp * (g2 + mucomp * (g3 + mucomp * g4)));
|
||||
return (1.0 + r * GoverPi) / (1.0 + constant1_FON * r);
|
||||
}
|
||||
|
||||
vec3 eonDiffuseBRDF(const vec3 albedo, const float roughness,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
float r = roughness;
|
||||
float mu_i = clamp(dotNL, 0.0, 1.0);
|
||||
float mu_o = clamp(dotNV, 0.0, 1.0);
|
||||
if (mu_i < 1.0e-7 || mu_o < 1.0e-7) return vec3(0.0);
|
||||
float dotLV = 2.0 * dotVH * dotVH - 1.0;
|
||||
float s = dotLV - mu_i * mu_o;
|
||||
float sovertF = s > 0.0 ? s / max(mu_i, mu_o) : s;
|
||||
float AF = 1.0 / (1.0 + constant1_FON * r);
|
||||
vec3 f_ss = (albedo * INV_PI) * AF * (1.0 + r * sovertF);
|
||||
float EFo = E_FON_approx(mu_o, r);
|
||||
float EFi = E_FON_approx(mu_i, r);
|
||||
float avgEF = AF * (1.0 + constant2_FON * r);
|
||||
vec3 rho_ms = (albedo * albedo) * avgEF
|
||||
/ max(vec3(1.0) - albedo * (1.0 - avgEF), vec3(1.0e-7));
|
||||
const float eps = 1.0e-7;
|
||||
vec3 f_ms = (rho_ms * INV_PI)
|
||||
* max(eps, 1.0 - EFo)
|
||||
* max(eps, 1.0 - EFi)
|
||||
/ max(eps, 1.0 - avgEF);
|
||||
return (f_ss + f_ms) * mu_i;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _GotandaDiffuse
|
||||
vec3 gotandaDiffuseBRDF(const vec3 albedo, const float roughness, const vec3 f0,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
float nl = clamp(dotNL, 0.0, 1.0);
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float dotLV = 2.0 * dotVH * dotVH - 1.0;
|
||||
float Cosri = dotLV - nv * nl;
|
||||
float a2_13 = a2 + 1.36053;
|
||||
float Fr = (1.0 - (0.542026 * a2 + 0.303573 * a) / a2_13)
|
||||
* (1.0 - pow(1.0 - nv, 5.0 - 4.0 * a2) / a2_13)
|
||||
* ((-0.733996 * a2 * a + 1.50912 * a2 - 1.16402 * a)
|
||||
* pow(1.0 - nv, 1.0 + 1.0 / (39.0 * a2 * a2 + 1.0)) + 1.0);
|
||||
float Lm = (max(1.0 - 2.0 * a, 0.0) * (1.0 - pow(1.0 - nl, 5.0))
|
||||
+ min(2.0 * a, 1.0)) * (1.0 - 0.5 * a * (nl - 1.0)) * nl;
|
||||
float Vd = (a2 / ((a2 + 0.09) * (1.31072 + 0.995584 * nv)))
|
||||
* (1.0 - pow(1.0 - nl,
|
||||
(1.0 - 0.3726732 * nv * nv)
|
||||
/ (0.188566 + 0.38841 * nv)));
|
||||
float Bp = Cosri < 0.0 ? 1.4 * nv * nl * Cosri : Cosri;
|
||||
vec3 Lr = (21.0 / 20.0) * (1.0 - f0) * (Fr * Lm + Vd + Bp);
|
||||
return max(albedo * INV_PI * Lr, vec3(0.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ChanDiffuse
|
||||
vec3 chanDiffuseBRDF(const vec3 albedo, const float roughness,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
float nl = clamp(dotNL, 0.0, 1.0);
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float vh = clamp(dotVH, 0.0, 1.0);
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float g = clamp((1.0 / 18.0) * log2(2.0 / max(a2, 1e-7) - 1.0), 0.0, 1.0);
|
||||
float dotNH = clamp((nl + nv) / max(2.0 * vh, 1e-5), 0.0, 1.0);
|
||||
float F0 = vh + pow(1.0 - vh, 5.0);
|
||||
float FdV = 1.0 - 0.75 * pow(1.0 - nv, 5.0);
|
||||
float FdL = 1.0 - 0.75 * pow(1.0 - nl, 5.0);
|
||||
float Fd = mix(F0, FdV * FdL, clamp(2.2 * g - 0.5, 0.0, 1.0));
|
||||
float Fb = ((34.5 * g - 59.0) * g + 24.5) * vh
|
||||
* exp2(-max(73.2 * g - 21.2, 8.9) * sqrt(dotNH));
|
||||
float Lobe = clamp(Fd + Fb, 0.0, 1.0);
|
||||
return albedo * INV_PI * Lobe * nl;
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 diffuseBRDF(const vec3 albedo, const float roughness, const vec3 f0,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
#ifdef _BurleyDiffuse
|
||||
return burleyDiffuseBRDF(albedo, roughness, dotNL, dotNV, dotVH);
|
||||
#elif defined(_EONDiffuse)
|
||||
return eonDiffuseBRDF(albedo, roughness, dotNL, dotNV, dotVH);
|
||||
#elif defined(_GotandaDiffuse)
|
||||
return gotandaDiffuseBRDF(albedo, roughness, f0, dotNL, dotNV, dotVH);
|
||||
#elif defined(_ChanDiffuse)
|
||||
return chanDiffuseBRDF(albedo, roughness, dotNL, dotNV, dotVH);
|
||||
#else
|
||||
return lambertDiffuseBRDF(albedo, dotNL);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 lambertDiffuseIBL(const vec3 albedo) {
|
||||
return albedo;
|
||||
}
|
||||
|
||||
#ifdef _BurleyDiffuse
|
||||
vec3 burleyDiffuseIBL(const vec3 albedo, const float roughness, const float dotNV) {
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float energyBias = mix(0.0, 0.5, roughness);
|
||||
float energyFactor = mix(1.0, 1.0 / 1.51, roughness);
|
||||
float fd90 = energyBias + 2.0 * roughness * nv * nv;
|
||||
float viewScatter = 1.0 + (fd90 - 1.0) * pow(1.0 - nv, 5.0);
|
||||
return albedo * viewScatter * energyFactor;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _EONDiffuse
|
||||
vec3 eonDiffuseIBL(const vec3 albedo, const float roughness, const float dotNV) {
|
||||
float r = roughness;
|
||||
float AF = 1.0 / (1.0 + constant1_FON * r);
|
||||
float EF = E_FON_approx(clamp(dotNV, 0.0, 1.0), r);
|
||||
float avgEF = AF * (1.0 + constant2_FON * r);
|
||||
vec3 rho_ms = (albedo * albedo) * avgEF
|
||||
/ max(vec3(1.0) - albedo * (1.0 - avgEF), vec3(1.0e-7));
|
||||
return max(albedo * EF + rho_ms * (1.0 - EF), vec3(0.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _GotandaDiffuse
|
||||
vec3 gotandaDiffuseIBL(const vec3 albedo, const float roughness, const vec3 f0, const float dotNV) {
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float a2_13 = a2 + 1.36053;
|
||||
float Fr = (1.0 - (0.542026 * a2 + 0.303573 * a) / a2_13)
|
||||
* (1.0 - pow(1.0 - nv, 5.0 - 4.0 * a2) / a2_13)
|
||||
* ((-0.733996 * a2 * a + 1.50912 * a2 - 1.16402 * a)
|
||||
* pow(1.0 - nv, 1.0 + 1.0 / (39.0 * a2 * a2 + 1.0)) + 1.0);
|
||||
float Lm = (max(1.0 - 2.0 * a, 0.0) * (1.0 - pow(1.0 - nv, 5.0))
|
||||
+ min(2.0 * a, 1.0)) * (1.0 - 0.5 * a * (nv - 1.0)) * nv;
|
||||
float Vd = (a2 / ((a2 + 0.09) * (1.31072 + 0.995584 * nv)))
|
||||
* (1.0 - pow(1.0 - nv,
|
||||
(1.0 - 0.3726732 * nv * nv)
|
||||
/ (0.188566 + 0.38841 * nv)));
|
||||
float Cosri = 1.0 - nv * nv;
|
||||
float Bp = Cosri;
|
||||
vec3 Lr = (21.0 / 20.0) * (1.0 - f0) * (Fr * Lm + Vd + Bp);
|
||||
return max(albedo * Lr, vec3(0.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ChanDiffuse
|
||||
vec3 chanDiffuseIBL(const vec3 albedo, const float roughness, const float dotNV) {
|
||||
float nv = clamp(dotNV, 0.0, 1.0);
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float g = clamp((1.0 / 18.0) * log2(2.0 / max(a2, 1e-7) - 1.0), 0.0, 1.0);
|
||||
float FdV = 1.0 - 0.75 * pow(1.0 - nv, 5.0);
|
||||
float Fd = mix(1.0, FdV * FdV, clamp(2.2 * g - 0.5, 0.0, 1.0));
|
||||
float Fb = ((34.5 * g - 59.0) * g + 24.5)
|
||||
* exp2(-max(73.2 * g - 21.2, 8.9) * sqrt(nv));
|
||||
return albedo * clamp(Fd + Fb, 0.0, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 diffuseIBL(const vec3 albedo, const float roughness, const vec3 f0, const float dotNV) {
|
||||
#ifdef _BurleyDiffuse
|
||||
return burleyDiffuseIBL(albedo, roughness, dotNV);
|
||||
#elif defined(_EONDiffuse)
|
||||
return eonDiffuseIBL(albedo, roughness, dotNV);
|
||||
#elif defined(_GotandaDiffuse)
|
||||
return gotandaDiffuseIBL(albedo, roughness, f0, dotNV);
|
||||
#elif defined(_ChanDiffuse)
|
||||
return chanDiffuseIBL(albedo, roughness, dotNV);
|
||||
#else
|
||||
return lambertDiffuseIBL(albedo);
|
||||
#endif
|
||||
}
|
||||
|
||||
vec3 surfaceAlbedo(const vec3 baseColor, const float metalness) {
|
||||
@ -95,24 +294,6 @@ float getMipFromRoughness(const float roughness, const float numMipmaps) {
|
||||
return roughness * numMipmaps;
|
||||
}
|
||||
|
||||
float wardSpecular(vec3 N, vec3 H, float dotNL, float dotNV, float dotNH, vec3 fiberDirection, float shinyParallel, float shinyPerpendicular) {
|
||||
if(dotNL < 0.0 || dotNV < 0.0) {
|
||||
return 0.0;
|
||||
}
|
||||
// fiberDirection - parse from rotation
|
||||
// shinyParallel - roughness
|
||||
// shinyPerpendicular - anisotropy
|
||||
|
||||
vec3 fiberParallel = normalize(fiberDirection);
|
||||
vec3 fiberPerpendicular = normalize(cross(N, fiberDirection));
|
||||
float dotXH = dot(fiberParallel, H);
|
||||
float dotYH = dot(fiberPerpendicular, H);
|
||||
const float PI = 3.1415926535;
|
||||
float coeff = sqrt(dotNL/dotNV) / (4.0 * PI * shinyParallel * shinyPerpendicular);
|
||||
float theta = (pow(dotXH/shinyParallel, 2.0) + pow(dotYH/shinyPerpendicular, 2.0)) / (1.0 + dotNH);
|
||||
return clamp(coeff * exp(-2.0 * theta), 0.0, 1.0);
|
||||
}
|
||||
|
||||
// https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
|
||||
// vec3 EnvBRDFApprox(vec3 SpecularColor, float Roughness, float NoV) {
|
||||
// const vec4 c0 = { -1, -0.0275, -0.572, 0.022 };
|
||||
@ -138,4 +319,207 @@ float D_Approx(const float Roughness, const float RoL) {
|
||||
return rcp_a2 * exp2( c * RoL - c );
|
||||
}
|
||||
|
||||
#ifdef _ClearCoat
|
||||
float brdf_coatF0;
|
||||
vec3 clearcoatBRDF(const float clearcoat, const float clearcoat_rough,
|
||||
const float coat_ior, const vec3 coatN, const vec3 l, const vec3 v, const vec3 h) {
|
||||
if (clearcoat <= 0.0) return vec3(0.0);
|
||||
float cdotNL = max(0.0, dot(coatN, l));
|
||||
float cdotNH = max(0.0, dot(coatN, h));
|
||||
float cdotNV = max(0.0, dot(coatN, v));
|
||||
float cdotVH = max(0.0, dot(v, h));
|
||||
float a = clearcoat_rough * clearcoat_rough;
|
||||
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotVH + SCHLICK_B) * cdotVH);
|
||||
float D = d_ggx(cdotNH, a);
|
||||
float G = g2_approx(cdotNL, cdotNV, a);
|
||||
return vec3(clearcoat * D * G * F / max(4.0 * cdotNV, 1e-5));
|
||||
}
|
||||
|
||||
float coatAttenuation(const float clearcoat,
|
||||
const float coat_ior, const vec3 coatN, const vec3 v) {
|
||||
if (clearcoat <= 0.0) return 1.0;
|
||||
float cdotNV = max(0.0, dot(coatN, v));
|
||||
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * cdotNV + SCHLICK_B) * cdotNV);
|
||||
return max(1.0 - F * clearcoat, 0.0);
|
||||
}
|
||||
|
||||
vec3 coatTintAttenuation(const float clearcoat, const vec3 coat_tint, const vec3 coatN, const vec3 v) {
|
||||
if (clearcoat <= 0.0) return vec3(1.0);
|
||||
float cdotNV = max(0.0, dot(coatN, v));
|
||||
float absorption = 1.0 / max(cdotNV, 0.3);
|
||||
return mix(vec3(1.0), clamp(coat_tint, 0.0, 1.0), clamp(absorption * 0.2, 0.0, 1.0));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Sheen
|
||||
float brdf_sheenAlbedo;
|
||||
// based on Blender sheen model/Frostbite PBR
|
||||
vec3 sheenBRDF(const float sheen, const float sheen_rough,
|
||||
const vec3 sheen_tint, const float dotNL, const float dotNH, const float dotNV) {
|
||||
if (sheen <= 0.0) return vec3(0.0);
|
||||
float rough = clamp(sheen_rough, 1e-3, 1.0);
|
||||
float a = rough * rough;
|
||||
float sinNH2 = 1.0 - dotNH * dotNH;
|
||||
float a2 = a * a;
|
||||
float denom = 1.0 + a2 * sinNH2;
|
||||
float D = (2.0 + a2) * sinNH2 * INV_TWO_PI / (denom * denom);
|
||||
float V = 1.0 / (4.0 * dotNL * dotNV + 1e-5);
|
||||
return sheen_tint * sheen * D * V * dotNL * brdf_sheenAlbedo;
|
||||
}
|
||||
|
||||
float sheenAttenuation(const float sheen, const float sheen_rough,
|
||||
const vec3 sheen_tint, const float dotNV) {
|
||||
if (sheen <= 0.0) return 1.0;
|
||||
float maxComp = sheen * max(max(sheen_tint.r, sheen_tint.g), sheen_tint.b) * brdf_sheenAlbedo;
|
||||
return max(1.0 - maxComp, 0.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
// anisotropic GGX Burley 2012
|
||||
vec3 anisotropicBRDF(const vec3 f0, const float roughness, const float anisotropy,
|
||||
const float aniso_rot, const vec3 tangent, const vec3 bitangent,
|
||||
const vec3 n, const vec3 l, const vec3 v,
|
||||
const float dotNL, const float dotNV) {
|
||||
if (abs(anisotropy) <= 0.001) return vec3(0.0);
|
||||
float rot = aniso_rot * PI * 2.0;
|
||||
float cr = cos(rot);
|
||||
float sr = sin(rot);
|
||||
vec3 t = normalize(tangent * cr + bitangent * sr);
|
||||
vec3 b = normalize(bitangent * cr - tangent * sr);
|
||||
float aniso_abs = abs(anisotropy);
|
||||
float at = max(roughness * (1.0 + aniso_abs), 1e-5);
|
||||
float ab = max(roughness * (1.0 - aniso_abs), 1e-5);
|
||||
if (anisotropy < 0.0) { vec3 tmp = t; t = b; b = tmp; }
|
||||
float at2 = at * at;
|
||||
float ab2 = ab * ab;
|
||||
vec3 h = normalize(l + v);
|
||||
float dotTH = dot(t, h);
|
||||
float dotBH = dot(b, h);
|
||||
float dotTV = dot(t, v);
|
||||
float dotBV = dot(b, v);
|
||||
float dotTL = dot(t, l);
|
||||
float dotBL = dot(b, l);
|
||||
float denom = max(dotTH * dotTH / at2 + dotBH * dotBH / ab2, 1e-7);
|
||||
float D = INV_PI / (at * ab * denom * denom);
|
||||
float V = 1.0 / max(dotNL * (dotTL / at + dotBL / ab) * (dotTV / at + dotBV / ab), 1e-5);
|
||||
float dotVH = max(dot(v, h), 0.0);
|
||||
vec3 F = f_schlick(f0, dotVH);
|
||||
return D * V * F / max(4.0 * dotNV, 1e-5);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Transmission
|
||||
float brdf_transmissionF0;
|
||||
// Blenders microfacet glass/refraction model
|
||||
vec3 transmissionBRDF(const vec3 albedo, const float transmission,
|
||||
const float trans_rough, const float ior, const float thin_wall,
|
||||
const float dotNL, const float dotNV, const float dotVH) {
|
||||
if (transmission <= 0.0) return vec3(0.0);
|
||||
float F = brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotVH + SCHLICK_B) * dotVH);
|
||||
float transmittance = 1.0 - F;
|
||||
if (thin_wall > 0.5) {
|
||||
return albedo * transmission * transmittance * dotNL;
|
||||
}
|
||||
float a = trans_rough * trans_rough;
|
||||
float rough_atten = min(mix(1.0, 1.0 / max(dotNV, 0.1), a), 4.0);
|
||||
return albedo * transmission * transmittance * rough_atten * dotNL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
float brdf_sheenWeight = 1.0;
|
||||
float brdf_coatWeight = 1.0;
|
||||
vec3 brdf_coatTintAbsorb = vec3(1.0);
|
||||
|
||||
vec3 applyExtBRDFLayers(
|
||||
const vec3 direct,
|
||||
const vec3 albedo,
|
||||
const vec3 f0,
|
||||
const float roughness,
|
||||
const float dotNL, const float dotNV, const float dotNH, const float dotVH,
|
||||
const vec3 n, const vec3 l, const vec3 v, const vec3 h,
|
||||
#ifdef _ClearCoat
|
||||
const float clearcoat, const float clearcoatRough, const float coatIOR,
|
||||
const vec3 coatTint, const vec3 coatN,
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
const float sheen, const float sheenRough, const vec3 sheenTint,
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
const float transmission, const float transRough, const float ior, const float thinWall,
|
||||
#endif
|
||||
out float layerWeight
|
||||
) {
|
||||
float sheenWeight = brdf_sheenWeight;
|
||||
float coatWeight = brdf_coatWeight;
|
||||
#ifdef _Sheen
|
||||
vec3 sheenContrib = sheenBRDF(sheen, sheenRough, sheenTint, dotNL, dotNH, dotNV);
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
vec3 coatContrib = clearcoatBRDF(clearcoat, clearcoatRough, coatIOR, coatN, l, v, h);
|
||||
#endif
|
||||
layerWeight = sheenWeight * coatWeight;
|
||||
vec3 result = direct * layerWeight;
|
||||
#ifdef _Transmission
|
||||
result += transmissionBRDF(albedo, transmission, transRough, ior, thinWall, dotNL, dotNV, dotVH) * layerWeight;
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
result *= brdf_coatTintAbsorb;
|
||||
result += coatContrib * sheenWeight;
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
result += sheenContrib;
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ClearCoat
|
||||
float coatIBLFresnel(const float clearcoat, const float coat_ior,
|
||||
const float dotNV_coat) {
|
||||
if (clearcoat <= 0.0) return 0.0;
|
||||
float F = brdf_coatF0 + (1.0 - brdf_coatF0) * exp2((SCHLICK_A * dotNV_coat + SCHLICK_B) * dotNV_coat);
|
||||
return F * clearcoat;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Sheen
|
||||
float sheenIBLAlbedo(const float sheen, const float sheen_rough,
|
||||
const float dotNV) {
|
||||
if (sheen <= 0.0) return 0.0;
|
||||
float rough = clamp(sheen_rough, 1e-3, 1.0);
|
||||
return sheen * (1.0 - 0.5 * rough) * mix(1.0, dotNV, 0.5);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Anisotropy
|
||||
vec3 anisotropicIBLDirection(const vec3 n, const vec3 v, const vec3 tangent,
|
||||
const float anisotropy, const float roughness) {
|
||||
if (abs(anisotropy) <= 0.001 || dot(tangent, tangent) < 0.001)
|
||||
return reflect(-v, n);
|
||||
vec3 bitangent = normalize(cross(n, tangent));
|
||||
vec3 r = reflect(-v, n);
|
||||
float aniso_abs = abs(anisotropy);
|
||||
vec3 stretchDir = anisotropy > 0.0 ? tangent : bitangent;
|
||||
float stretchAmt = aniso_abs * roughness;
|
||||
return normalize(r + stretchDir * stretchAmt * dot(r, stretchDir) * 0.5);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _Transmission
|
||||
float transmissionIBLFresnel(const float ior, const float dotNV) {
|
||||
return brdf_transmissionF0 + (1.0 - brdf_transmissionF0) * exp2((SCHLICK_A * dotNV + SCHLICK_B) * dotNV);
|
||||
}
|
||||
|
||||
vec3 transmissionIBLDirection(const vec3 n, const vec3 v, const float ior) {
|
||||
float eta = 1.0 / ior;
|
||||
vec3 refrDir = refract(-v, n, eta);
|
||||
if (dot(refrDir, refrDir) < 0.001) {
|
||||
refrDir = reflect(-v, n);
|
||||
}
|
||||
return refrDir;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@ -34,17 +34,19 @@ THE SOFTWARE.
|
||||
// https://research.nvidia.com/sites/default/files/publications/GIVoxels-pg2011-authors.pdf
|
||||
|
||||
const float MAX_DISTANCE = voxelgiRange;
|
||||
const int MAX_CONE_STEPS = 32;
|
||||
|
||||
#ifdef _VoxelGI
|
||||
uniform sampler3D dummy;
|
||||
|
||||
vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
|
||||
vec4 col = vec4(0.0);
|
||||
vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
int base = int(clipmap_index * 10);
|
||||
float voxelSize = float(clipmaps[base]);
|
||||
vec3 tc = (P - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution);
|
||||
vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
tc = tc * 0.5 + 0.5;
|
||||
tc = clamp(tc, half_texel, 1.0 - half_texel);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + diffuseConeCount);
|
||||
tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
|
||||
|
||||
if (precomputed_direction == 0) {
|
||||
@ -55,7 +57,7 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo
|
||||
else
|
||||
col = textureLod(voxels, tc, 0);
|
||||
|
||||
col *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
|
||||
col *= step_dist / voxelSize;
|
||||
|
||||
return col;
|
||||
}
|
||||
@ -64,11 +66,13 @@ vec4 sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCo
|
||||
#ifdef _VoxelAOvar
|
||||
float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapCount * 10], const float clipmap_index, const float step_dist, const int precomputed_direction, const vec3 face_offset, const vec3 direction_weight) {
|
||||
float opac = 0.0;
|
||||
vec3 tc = (P - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
int base = int(clipmap_index * 10);
|
||||
float voxelSize = float(clipmaps[base]);
|
||||
vec3 tc = (P - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution);
|
||||
vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
tc = tc * 0.5 + 0.5;
|
||||
tc = clamp(tc, half_texel, 1.0 - half_texel);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + DIFFUSE_CONE_COUNT);
|
||||
tc.x = (tc.x + precomputed_direction) / (6 + diffuseConeCount);
|
||||
tc.y = (tc.y + clipmap_index) / voxelgiClipmapCount;
|
||||
|
||||
if (precomputed_direction == 0) {
|
||||
@ -79,7 +83,7 @@ float sampleVoxel(sampler3D voxels, vec3 P, const float clipmaps[voxelgiClipmapC
|
||||
else
|
||||
opac = textureLod(voxels, tc, 0).r;
|
||||
|
||||
opac *= step_dist / float(clipmaps[int(clipmap_index * 10)]);
|
||||
opac *= step_dist / voxelSize;
|
||||
|
||||
return opac;
|
||||
}
|
||||
@ -92,7 +96,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
@ -100,20 +104,24 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
||||
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
) / (6 + diffuseConeCount);
|
||||
vec3 direction_weight = abs(dir);
|
||||
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
int steps = 0;
|
||||
while (sampleCol.a < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) {
|
||||
vec4 mipSample = vec4(0.0);
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
int base = int(clipmap_index * 10);
|
||||
float voxelSize = float(clipmaps[base]);
|
||||
samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if (any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0)))) {
|
||||
@ -121,19 +129,26 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
||||
continue;
|
||||
}
|
||||
|
||||
// Edge fade: blend toward coarser clipmap near boundaries
|
||||
vec3 edgeDist = min(samplePos, 1.0 - samplePos);
|
||||
float minEdgeDist = min(min(edgeDist.x, edgeDist.y), edgeDist.z);
|
||||
float edgeBlend = 1.0 - smoothstep(0.0, 0.1, minEdgeDist);
|
||||
float totalBlend = max(clipmap_blend, edgeBlend);
|
||||
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
vec4 mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
|
||||
int baseNext = int((clipmap_index + 1.0) * 10);
|
||||
float voxelSizeCoarse = float(clipmaps[baseNext]);
|
||||
mipSampleNext *= voxelSizeCoarse / voxelSize;
|
||||
mipSample = mix(mipSample, mipSampleNext, totalBlend);
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol.a) * mipSample;
|
||||
|
||||
float stepSizeCurrent = step_size;
|
||||
if (use_sdf) {
|
||||
// half texel correction is applied to avoid sampling over current clipmap:
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
|
||||
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
|
||||
float sdf = textureLod(voxelsSDF, tc0, 0).r;
|
||||
@ -141,6 +156,7 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
||||
}
|
||||
step_dist = diam * stepSizeCurrent;
|
||||
dist += step_dist;
|
||||
steps++;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
@ -148,18 +164,17 @@ vec4 traceCone(const sampler3D voxels, const sampler3D voxelsSDF, const vec3 ori
|
||||
vec4 traceDiffuse(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sum = 0.0;
|
||||
vec4 amount = vec4(0.0);
|
||||
mat3 TBN = makeTangentBasis(normal);
|
||||
for (int i = 0; i < DIFFUSE_CONE_COUNT; ++i) {
|
||||
vec3 coneDir = TBN * DIFFUSE_CONE_DIRECTIONS[i];
|
||||
for (int i = 0; i < diffuseConeCount; ++i) {
|
||||
vec3 coneDir = diffuseConeDirections[i];
|
||||
const float cosTheta = dot(normal, coneDir);
|
||||
if (cosTheta <= 0)
|
||||
continue;
|
||||
int precomputed_direction = 6 + i;
|
||||
amount += traceCone(voxels, dummy, origin, normal, coneDir, precomputed_direction, false, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
|
||||
amount += traceCone(voxels, voxels, origin, normal, coneDir, precomputed_direction, false, diffuseConeAperture, 1.0, clipmaps) * cosTheta;
|
||||
sum += cosTheta;
|
||||
}
|
||||
|
||||
amount /= sum;
|
||||
amount /= max(sum, 0.0001);
|
||||
amount.rgb = max(amount.rgb, vec3(0.0));
|
||||
amount.a = clamp(amount.a, 0.0, 1.0);
|
||||
|
||||
@ -186,7 +201,7 @@ vec4 traceRefraction(const vec3 origin, const vec3 normal, sampler3D voxels, sam
|
||||
amount.rgb = max(vec3(0.0), amount.rgb);
|
||||
amount.a = clamp(amount.a, 0.0, 1.0);
|
||||
|
||||
return amount * voxelgiOcc;
|
||||
return amount * voxelgiOcc * voxelgiRefr;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -197,7 +212,7 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
@ -205,20 +220,23 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
|
||||
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
) / (6 + diffuseConeCount);
|
||||
vec3 direction_weight = abs(dir);
|
||||
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
int steps = 0;
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) {
|
||||
float mipSample = 0.0;
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution.x);
|
||||
int base = int(clipmap_index * 10);
|
||||
float voxelSize = float(clipmaps[base]);
|
||||
samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if ((any(notEqual(clamp(samplePos, 0.0, 1.0), samplePos)))) {
|
||||
@ -226,17 +244,27 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
|
||||
continue;
|
||||
}
|
||||
|
||||
// Edge fade: blend toward coarser clipmap near boundaries
|
||||
vec3 edgeDist = min(samplePos, 1.0 - samplePos);
|
||||
float minEdgeDist = min(min(edgeDist.x, edgeDist.y), edgeDist.z);
|
||||
float edgeBlend = 1.0 - smoothstep(0.0, 0.1, minEdgeDist);
|
||||
float totalBlend = max(clipmap_blend, edgeBlend);
|
||||
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, precomputed_direction, face_offset, direction_weight);
|
||||
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
|
||||
int baseNext = int((clipmap_index + 1.0) * 10);
|
||||
float voxelSizeCoarse = float(clipmaps[baseNext]);
|
||||
mipSampleNext *= voxelSizeCoarse / voxelSize;
|
||||
mipSample = mix(mipSample, mipSampleNext, totalBlend);
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol) * mipSample;
|
||||
|
||||
step_dist = diam * step_size;
|
||||
dist += step_dist;
|
||||
steps++;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
@ -245,18 +273,18 @@ float traceConeAO(const sampler3D voxels, const vec3 origin, const vec3 n, const
|
||||
float traceAO(const vec3 origin, const vec3 normal, const sampler3D voxels, const float clipmaps[voxelgiClipmapCount * 10]) {
|
||||
float sum = 0.0;
|
||||
float amount = 0.0;
|
||||
for (int i = 0; i < DIFFUSE_CONE_COUNT; i++) {
|
||||
vec3 coneDir = DIFFUSE_CONE_DIRECTIONS[i];
|
||||
for (int i = 0; i < diffuseConeCount; i++) {
|
||||
vec3 coneDir = diffuseConeDirections[i];
|
||||
int precomputed_direction = 6 + i;
|
||||
const float cosTheta = dot(normal, coneDir);
|
||||
if (cosTheta <= 0)
|
||||
continue;
|
||||
amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, DIFFUSE_CONE_APERTURE, 1.0, clipmaps) * cosTheta;
|
||||
amount += traceConeAO(voxels, origin, normal, coneDir, precomputed_direction, diffuseConeAperture, 1.0, clipmaps) * cosTheta;
|
||||
sum += cosTheta;
|
||||
}
|
||||
amount /= sum;
|
||||
amount /= max(sum, 0.0001);
|
||||
amount = clamp(amount, 0.0, 1.0);
|
||||
return amount * voxelgiOcc;
|
||||
return amount;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -267,7 +295,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
||||
float dist = voxelSize0;
|
||||
float step_dist = dist;
|
||||
vec3 samplePos;
|
||||
vec3 start_pos = origin + n * voxelSize0;
|
||||
vec3 start_pos = origin + n * voxelSize0 * voxelgiOffset;
|
||||
int clipmap_index0 = 0;
|
||||
|
||||
vec3 aniso_direction = -dir;
|
||||
@ -275,19 +303,23 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
||||
aniso_direction.x > 0.0 ? 0.0 : 1.0,
|
||||
aniso_direction.y > 0.0 ? 2.0 : 3.0,
|
||||
aniso_direction.z > 0.0 ? 4.0 : 5.0
|
||||
) / (6 + DIFFUSE_CONE_COUNT);
|
||||
) / (6 + diffuseConeCount);
|
||||
vec3 direction_weight = abs(dir);
|
||||
float coneCoefficient = 2.0 * tan(aperture * 0.5);
|
||||
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount) {
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
int steps = 0;
|
||||
while (sampleCol < 1.0 && dist < MAX_DISTANCE && clipmap_index0 < voxelgiClipmapCount && steps < MAX_CONE_STEPS) {
|
||||
float mipSample = 0.0;
|
||||
float diam = max(voxelSize0, dist * coneCoefficient);
|
||||
float lod = clamp(log2(diam / voxelSize0), clipmap_index0, voxelgiClipmapCount - 1);
|
||||
float clipmap_index = floor(lod);
|
||||
float clipmap_blend = fract(lod);
|
||||
float clipmap_blend = smoothstep(0.0, 1.0, fract(lod));
|
||||
vec3 p0 = start_pos + dir * dist;
|
||||
|
||||
samplePos = (p0 - vec3(clipmaps[int(clipmap_index * 10 + 4)], clipmaps[int(clipmap_index * 10 + 5)], clipmaps[int(clipmap_index * 10 + 6)])) / (float(clipmaps[int(clipmap_index * 10)]) * voxelgiResolution);
|
||||
int base = int(clipmap_index * 10);
|
||||
float voxelSize = float(clipmaps[base]);
|
||||
samplePos = (p0 - vec3(clipmaps[base + 4], clipmaps[base + 5], clipmaps[base + 6])) / (voxelSize * voxelgiResolution);
|
||||
samplePos = samplePos * 0.5 + 0.5;
|
||||
|
||||
if ((any(notEqual(samplePos, clamp(samplePos, 0.0, 1.0))))) {
|
||||
@ -295,27 +327,34 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
||||
continue;
|
||||
}
|
||||
|
||||
// Edge fade: blend toward coarser clipmap near boundaries
|
||||
vec3 edgeDist = min(samplePos, 1.0 - samplePos);
|
||||
float minEdgeDist = min(min(edgeDist.x, edgeDist.y), edgeDist.z);
|
||||
float edgeBlend = 1.0 - smoothstep(0.0, 0.1, minEdgeDist);
|
||||
float totalBlend = max(clipmap_blend, edgeBlend);
|
||||
|
||||
#ifdef _VoxelAOvar
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight);
|
||||
#else
|
||||
mipSample = sampleVoxel(voxels, p0, clipmaps, clipmap_index, step_dist, 0, face_offset, direction_weight).a;
|
||||
#endif
|
||||
|
||||
if(clipmap_blend > 0.0 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
if(totalBlend > 0.05 && clipmap_index < voxelgiClipmapCount - 1) {
|
||||
int baseNext = int((clipmap_index + 1.0) * 10);
|
||||
float voxelSizeCoarse = float(clipmaps[baseNext]);
|
||||
float scaleRatio = voxelSizeCoarse / voxelSize;
|
||||
#ifdef _VoxelAOvar
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight);
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight) * scaleRatio;
|
||||
#else
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a;
|
||||
float mipSampleNext = sampleVoxel(voxels, p0, clipmaps, clipmap_index + 1.0, step_dist, 0, face_offset, direction_weight).a * scaleRatio;
|
||||
#endif
|
||||
mipSample = mix(mipSample, mipSampleNext, clipmap_blend);
|
||||
mipSample = mix(mipSample, mipSampleNext, totalBlend);
|
||||
}
|
||||
|
||||
sampleCol += (1.0 - sampleCol) * mipSample;
|
||||
|
||||
float stepSizeCurrent = step_size;
|
||||
|
||||
// half texel correction is applied to avoid sampling over current clipmap:
|
||||
const vec3 half_texel = vec3(0.5) / voxelgiResolution;
|
||||
vec3 tc0 = clamp(samplePos, half_texel, 1 - half_texel);
|
||||
tc0.y = (tc0.y + clipmap_index) / voxelgiClipmapCount; // remap into clipmap
|
||||
float sdf = textureLod(voxelsSDF, tc0, 0.0).r;
|
||||
@ -323,6 +362,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
||||
|
||||
step_dist = diam * stepSizeCurrent;
|
||||
dist += step_dist;
|
||||
steps++;
|
||||
}
|
||||
return sampleCol;
|
||||
}
|
||||
@ -330,7 +370,7 @@ float traceConeShadow(const sampler3D voxels, const sampler3D voxelsSDF, const v
|
||||
|
||||
float traceShadow(const vec3 origin, const vec3 normal, const sampler3D voxels, const sampler3D voxelsSDF, const vec3 dir, const float clipmaps[voxelgiClipmapCount * 10], const vec2 pixel, const vec2 velocity) {
|
||||
vec3 P = origin + dir * (BayerMatrix8[int(pixel.x + velocity.x) % 8][int(pixel.y + velocity.y) % 8] - 0.5) * voxelgiStep;
|
||||
float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, SHADOW_CONE_APERTURE, voxelgiStep, clipmaps);
|
||||
float amount = traceConeShadow(voxels, voxelsSDF, P, normal, dir, voxelgiAperture, voxelgiStep, clipmaps);
|
||||
amount = clamp(amount, 0.0, 1.0);
|
||||
return amount * voxelgiOcc;
|
||||
}
|
||||
|
||||
@ -20,46 +20,25 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
const int DIFFUSE_CONE_COUNT = 16;
|
||||
const float diffuseConeAperture = radians(39.0);
|
||||
|
||||
const float SHADOW_CONE_APERTURE = radians(15.0);
|
||||
|
||||
const float DIFFUSE_CONE_APERTURE = 0.872665;
|
||||
|
||||
mat3 makeTangentBasis(const vec3 normal) {
|
||||
// Create a tangent basis from normal vector
|
||||
vec3 tangent;
|
||||
vec3 bitangent;
|
||||
|
||||
// Compute tangent (Frisvad's method)
|
||||
if (abs(normal.z) < 0.999) {
|
||||
tangent = normalize(cross(vec3(0, 1, 0), normal));
|
||||
} else {
|
||||
tangent = normalize(cross(normal, vec3(1, 0, 0)));
|
||||
}
|
||||
bitangent = cross(normal, tangent);
|
||||
|
||||
return mat3(tangent, bitangent, normal);
|
||||
}
|
||||
|
||||
// 16 optimized cone directions for hemisphere sampling (Z-up, normalized)
|
||||
const vec3 DIFFUSE_CONE_DIRECTIONS[16] = vec3[](
|
||||
vec3(0.707107, 0.000000, 0.707107), // Front
|
||||
vec3(-0.707107, 0.000000, 0.707107), // Back
|
||||
vec3(0.000000, 0.707107, 0.707107), // Right
|
||||
vec3(0.000000, -0.707107, 0.707107), // Left
|
||||
vec3(0.500000, 0.500000, 0.707107), // Front-right
|
||||
vec3(-0.500000, 0.500000, 0.707107), // Back-right
|
||||
vec3(0.500000, -0.500000, 0.707107), // Front-left
|
||||
vec3(-0.500000, -0.500000, 0.707107),// Back-left
|
||||
vec3(0.353553, 0.000000, 0.935414), // Narrow front
|
||||
vec3(-0.353553, 0.000000, 0.935414), // Narrow back
|
||||
vec3(0.000000, 0.353553, 0.935414), // Narrow right
|
||||
vec3(0.000000, -0.353553, 0.935414), // Narrow left
|
||||
vec3(0.270598, 0.270598, 0.923880), // Narrow front-right
|
||||
vec3(-0.270598, 0.270598, 0.923880), // Narrow back-right
|
||||
vec3(0.270598, -0.270598, 0.923880), // Narrow front-left
|
||||
vec3(-0.270598, -0.270598, 0.923880) // Narrow back-left
|
||||
const vec3 diffuseConeDirections[16] = vec3[](
|
||||
vec3( 0.3480, 0.0000, 0.9375),
|
||||
vec3(-0.4299, 0.3938, 0.8125),
|
||||
vec3( 0.0635, -0.7234, 0.6875),
|
||||
vec3( 0.5031, 0.6561, 0.5625),
|
||||
vec3(-0.8855, -0.1566, 0.4375),
|
||||
vec3( 0.8015, -0.5098, 0.3125),
|
||||
vec3(-0.2550, 0.9486, 0.1875),
|
||||
vec3(-0.4600, -0.8857, 0.0625),
|
||||
vec3( 0.9375, 0.3424, -0.0625),
|
||||
vec3(-0.9080, 0.3748, -0.1875),
|
||||
vec3( 0.4026, -0.8604, -0.3125),
|
||||
vec3( 0.2691, 0.8580, -0.4375),
|
||||
vec3(-0.7154, -0.4146, -0.5625),
|
||||
vec3( 0.7092, -0.1559, -0.6875),
|
||||
vec3(-0.3353, 0.4769, -0.8125),
|
||||
vec3(-0.0447, -0.3451, -0.9375)
|
||||
);
|
||||
|
||||
// TO DO - Disabled momentarily instead of changing formulas
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
const int samples = 8; // Samples on the first ring
|
||||
const int rings = 6; // Ring count
|
||||
const vec2 focus = vec2(0.5, 0.5);
|
||||
//const vec2 focus = vec2(0.5, 0.5);
|
||||
const float coc = 0.03; // Circle of confusion size in mm (35mm film = 0.03mm)
|
||||
const float maxblur = 1.0;
|
||||
//const float maxblur = 1.0;
|
||||
const float threshold = 0.5; // Highlight threshold
|
||||
const float gain = 2.0; // Highlight gain
|
||||
const float bias = 0.5; // Bokeh edge bias
|
||||
@ -41,7 +41,9 @@ vec3 dof(
|
||||
const bool autoFocus,
|
||||
const float DOFDistance,
|
||||
const float DOFLength,
|
||||
const float DOFFStop) {
|
||||
const float DOFFStop,
|
||||
const vec2 focus,
|
||||
const float maxblur) {
|
||||
|
||||
float depth = linearize(gdepth, cameraProj);
|
||||
float fDepth = 0.0;
|
||||
@ -85,7 +87,6 @@ vec3 dof(
|
||||
float pw = (cos(float(j) * step) * float(i));
|
||||
float ph = (sin(float(j) * step) * float(i));
|
||||
float p = 1.0;
|
||||
// if (pentagon) p = penta(vec2(pw, ph));
|
||||
blurredCol += color(texCoord + vec2(pw * w, ph * h), blur, tex, texStep) * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
s += 1.0 * mix(1.0, (float(i)) / (float(rings)), bias) * p;
|
||||
}
|
||||
|
||||
@ -170,4 +170,88 @@ void unpackFloatInt16(float val, out float f, out uint i) {
|
||||
f = (bitsValue & ~(0xF << numBitFloat)) / maxValFloat;
|
||||
}
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
// extended material parameters by material slot ID returns vec4s (28 floats) of extended BRDF parameters
|
||||
void getMaterialParams(uint matid, out vec4 p0, out vec4 p1, out vec4 p2, out vec4 p3,
|
||||
out vec4 p4, out vec4 p5, out vec4 p6, out vec4 p7) {
|
||||
uint base = matid * 8u;
|
||||
#if defined(_Anisotropy) || defined(_Sheen)
|
||||
p0 = materialParams[base];
|
||||
#else
|
||||
p0 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_ClearCoat)
|
||||
p1 = materialParams[base + 1u];
|
||||
#else
|
||||
p1 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_ClearCoat) || defined(_Transmission)
|
||||
p2 = materialParams[base + 2u];
|
||||
#else
|
||||
p2 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_Transmission) || defined(_SSS)
|
||||
p3 = materialParams[base + 3u];
|
||||
#else
|
||||
p3 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_SSS)
|
||||
p4 = materialParams[base + 4u];
|
||||
#else
|
||||
p4 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_Sheen) || defined(_SSS)
|
||||
p5 = materialParams[base + 5u];
|
||||
#else
|
||||
p5 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_ExtBRDF)
|
||||
p6 = materialParams[base + 6u];
|
||||
#else
|
||||
p6 = vec4(0.0);
|
||||
#endif
|
||||
#if defined(_SSS)
|
||||
p7 = materialParams[base + 7u];
|
||||
#else
|
||||
p7 = vec4(0.0);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float packIOR(float ior) {
|
||||
return clamp((ior - 1.0) / 1.5, 0.0, 1.0);
|
||||
}
|
||||
|
||||
float unpackIOR(float packed) {
|
||||
return packed * 1.5 + 1.0;
|
||||
}
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535
|
||||
#endif
|
||||
#ifndef PI2
|
||||
#define PI2 6.2831853071
|
||||
#endif
|
||||
|
||||
float encodeTangent(vec3 tangent, vec3 normal) {
|
||||
if (length(tangent) < 0.5) return -1.0;
|
||||
vec3 t = normalize(tangent);
|
||||
vec3 n = normalize(normal);
|
||||
vec3 ref = abs(n.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 r = normalize(ref - n * dot(ref, n));
|
||||
vec3 b = cross(n, r);
|
||||
float angle = atan(dot(t, b), dot(t, r));
|
||||
return (angle / (2.0 * PI) + 0.5);
|
||||
}
|
||||
|
||||
vec3 decodeTangent(float enc, vec3 normal) {
|
||||
if (enc < 0.0) return vec3(1.0, 0.0, 0.0);
|
||||
vec3 n = normalize(normal);
|
||||
vec3 ref = abs(n.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 r = normalize(ref - n * dot(ref, n));
|
||||
vec3 b = cross(n, r);
|
||||
float angle = (enc - 0.5) * 2.0 * PI;
|
||||
return normalize(r * cos(angle) + b * sin(angle));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -3,7 +3,9 @@ uniform sampler2D texIES;
|
||||
|
||||
float iesAttenuation(vec3 l) {
|
||||
|
||||
const float PI = 3.1415926535;
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535
|
||||
#endif
|
||||
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
||||
// Sample direction into light space
|
||||
// vec3 iesSampleDirection = mul(light.worldToLight , -L);
|
||||
|
||||
@ -14,31 +14,54 @@
|
||||
#ifdef _SinglePoint
|
||||
#ifdef _Spot
|
||||
uniform sampler2DShadow shadowMapSpot[1];
|
||||
uniform mat4 LWVPSpot[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[1];
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[1];
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[1];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform samplerCube shadowMapPointTransparent[1];
|
||||
#endif
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _SingleAtlas
|
||||
//!uniform sampler2DShadow shadowMapAtlas;
|
||||
#ifdef _ShadowMapTransparent
|
||||
//!uniform sampler2D shadowMapAtlasTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#ifndef _SinglePoint
|
||||
uniform vec2 lightProj;
|
||||
#endif
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasPoint;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasPointTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint[4];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform samplerCube shadowMapPointTransparent[4];
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
#ifdef _ShadowMapAtlas
|
||||
#ifndef _SingleAtlas
|
||||
uniform sampler2DShadow shadowMapAtlasSpot;
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapAtlasSpotTransparent;
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
uniform sampler2DShadow shadowMapSpot[maxLightsCluster];
|
||||
#ifdef _ShadowMapTransparent
|
||||
uniform sampler2D shadowMapSpotTransparent[maxLightsCluster];
|
||||
#endif
|
||||
#endif
|
||||
uniform mat4 LWVPSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
@ -49,23 +72,71 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
const vec3 albedo, const float rough, const float spec, const vec3 f0
|
||||
#ifdef _ShadowMap
|
||||
, int index, float bias, bool receiveShadow
|
||||
#ifdef _ShadowMapTransparent
|
||||
, bool transparent
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, float clearcoat, float clearcoatRough, float coatIOR, vec3 coatTint, vec3 coatN
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, float sheen, float sheenRough, vec3 sheenTint
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, float anisotropy, float anisoRot, vec3 tangent
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, float subsurface, vec3 sssColor, vec3 sssRadius, float sssAnisotropy
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, float transmission, float transRough, float ior, float thinWall
|
||||
#endif
|
||||
) {
|
||||
vec3 ld = lp - p;
|
||||
vec3 l = normalize(ld);
|
||||
float dist = length(ld);
|
||||
vec3 l = ld / dist;
|
||||
vec3 h = normalize(v + l);
|
||||
float dotNH = max(0.0, dot(n, h));
|
||||
float dotVH = max(0.0, dot(v, h));
|
||||
float dotNL = max(0.0, dot(n, l));
|
||||
|
||||
vec3 direct = lambertDiffuseBRDF(albedo, dotNL) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#ifdef _Anisotropy
|
||||
vec3 direct;
|
||||
if (abs(anisotropy) > 0.001 && dot(tangent, tangent) > 0.001) {
|
||||
vec3 bitangent = normalize(cross(n, tangent));
|
||||
direct = diffuseBRDF(albedo, rough, f0, dotNL, dotNV, dotVH) +
|
||||
anisotropicBRDF(f0, rough, anisotropy, anisoRot,
|
||||
tangent, bitangent, n, l, v, dotNL, dotNV) * spec;
|
||||
} else {
|
||||
direct = diffuseBRDF(albedo, rough, f0, dotNL, dotNV, dotVH) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
}
|
||||
#else
|
||||
vec3 direct = diffuseBRDF(albedo, rough, f0, dotNL, dotNV, dotVH) +
|
||||
specularBRDF(f0, rough, dotNL, dotNH, dotNV, dotVH) * spec;
|
||||
#endif
|
||||
|
||||
#ifdef _ExtBRDF
|
||||
float layerWeight;
|
||||
direct = applyExtBRDFLayers(direct, albedo, f0, rough, dotNL, dotNV, dotNH, dotVH, n, l, v, h
|
||||
#ifdef _ClearCoat
|
||||
, clearcoat, clearcoatRough, coatIOR, coatTint, coatN
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, sheen, sheenRough, sheenTint
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, transmission, transRough, ior, thinWall
|
||||
#endif
|
||||
, layerWeight
|
||||
);
|
||||
#endif
|
||||
|
||||
direct *= lightCol;
|
||||
direct *= attenuate(distance(p, lp));
|
||||
direct *= attenuate(dist);
|
||||
|
||||
#ifdef _Spot
|
||||
if (isSpot) {
|
||||
@ -74,25 +145,77 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
#ifdef _ShadowMap
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
vec4 lPos = LWVPSpot[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
vec4 lPos = LWVPSpotArray[0] * vec4(p + n * bias * 10, 1.0);
|
||||
direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
vec4 lPos = LWVPSpotArray[index] * vec4(p + n * bias * 10, 1.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= shadowTest(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
tileBounds = tileBoundsSpotArray[index];
|
||||
direct *= shadowTest(
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot, shadowMapAtlasSpotTransparent
|
||||
#else
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
#else
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasSpot
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[0],
|
||||
#endif
|
||||
, lPos.xyz / lPos.w, bias
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= shadowTest(shadowMapSpot[0], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2], lPos.xyz / lPos.w, bias);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3], lPos.xyz / lPos.w, bias);
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) direct *= shadowTest(shadowMapSpot[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[1],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) direct *= shadowTest(shadowMapSpot[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[2],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) direct *= shadowTest(shadowMapSpot[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapSpotTransparent[3],
|
||||
#endif
|
||||
lPos.xyz / lPos.w, bias
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@ -105,25 +228,76 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
|
||||
if (receiveShadow) {
|
||||
#ifdef _SinglePoint
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
#ifndef _Spot
|
||||
direct *= PCFCube(shadowMapPoint[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[0],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Clusters
|
||||
#ifdef _ShadowMapAtlas
|
||||
direct *= PCFFakeCube(
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index
|
||||
);
|
||||
direct *= PCFFakeCube(
|
||||
#ifdef _ShadowMapTransparent
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint, shadowMapAtlasPointTransparent
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0], ld, -l, bias, lightProj, n);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1], ld, -l, bias, lightProj, n);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2], ld, -l, bias, lightProj, n);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3], ld, -l, bias, lightProj, n);
|
||||
shadowMapAtlas, shadowMapAtlasTransparent
|
||||
#endif
|
||||
#else
|
||||
#ifndef _SingleAtlas
|
||||
shadowMapAtlasPoint
|
||||
#else
|
||||
shadowMapAtlas
|
||||
#endif
|
||||
#endif
|
||||
, ld, -l, bias, lightProj, n, index
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
if (index == 0) direct *= PCFCube(shadowMapPoint[0],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[0],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 1) direct *= PCFCube(shadowMapPoint[1],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[1],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 2) direct *= PCFCube(shadowMapPoint[2],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[2],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
else if (index == 3) direct *= PCFCube(shadowMapPoint[3],
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapPointTransparent[3],
|
||||
#endif
|
||||
ld, -l, bias, lightProj, n
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@ -132,4 +306,47 @@ vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, co
|
||||
return direct;
|
||||
}
|
||||
|
||||
// Backward-compatible overload for generated shaders that don't pass extended BRDF params
|
||||
#ifdef _ExtBRDF
|
||||
vec3 sampleLight(const vec3 p, const vec3 n, const vec3 v, const float dotNV, const vec3 lp, const vec3 lightCol,
|
||||
const vec3 albedo, const float rough, const float spec, const vec3 f0
|
||||
#ifdef _ShadowMap
|
||||
, int index, float bias, bool receiveShadow
|
||||
#ifdef _ShadowMapTransparent
|
||||
, bool transparent
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, bool isSpot, float spotSize, float spotBlend, vec3 spotDir, vec2 scale, vec3 right
|
||||
#endif
|
||||
) {
|
||||
return sampleLight(p, n, v, dotNV, lp, lightCol, albedo, rough, spec, f0
|
||||
#ifdef _ShadowMap
|
||||
, index, bias, receiveShadow
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
#endif
|
||||
#ifdef _Spot
|
||||
, isSpot, spotSize, spotBlend, spotDir, scale, right
|
||||
#endif
|
||||
#ifdef _ClearCoat
|
||||
, 0.0, 0.0, 1.5, vec3(1.0), n
|
||||
#endif
|
||||
#ifdef _Sheen
|
||||
, 0.0, 0.0, vec3(1.0)
|
||||
#endif
|
||||
#ifdef _Anisotropy
|
||||
, 0.0, 0.0, vec3(0.0)
|
||||
#endif
|
||||
#ifdef _SSS
|
||||
, 0.0, vec3(0.0), vec3(0.0), 0.0
|
||||
#endif
|
||||
#ifdef _Transmission
|
||||
, 0.0, 0.0, 1.45, 1.0
|
||||
#endif
|
||||
);
|
||||
}
|
||||
#endif // _ExtBRDF
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,41 +1,81 @@
|
||||
/*
|
||||
https://github.com/JonasFolletete/glsl-triplanar-mapping
|
||||
vec4 boxProjection(sampler2D image, vec3 normal, vec3 coord, float blend) {
|
||||
vec3 n = normalize(normal);
|
||||
vec3 N = abs(n);
|
||||
vec4 color1, color2, color3;
|
||||
|
||||
MIT License
|
||||
vec2 uv = coord.yz;
|
||||
if (n.x < 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color1 = texture(image, uv);
|
||||
|
||||
Copyright (c) 2018 Jonas Folletête
|
||||
uv = coord.xz;
|
||||
if (n.y > 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color2 = texture(image, uv);
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
uv = vec2(coord.y, 1.0 - coord.x);
|
||||
if (n.z > 0.0) {
|
||||
uv.x = 1.0 - uv.x;
|
||||
}
|
||||
color3 = texture(image, uv);
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
N /= max(dot(N, vec3(1.0)), 1e-8);
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
float limit = 0.5 + 0.5 * clamp(blend, 0.0, 1.0);
|
||||
vec3 weight;
|
||||
weight = N.xyz / (N.xyx + N.yzz);
|
||||
weight = clamp((weight - 0.5 * (1.0 - clamp(blend, 0.0, 1.0))) / max(1e-8, clamp(blend, 0.0, 1.0)), 0.0, 1.0);
|
||||
|
||||
vec3 blendNormal(vec3 normal) {
|
||||
vec3 blending = abs(normal);
|
||||
blending = normalize(max(blending, 0.00001));
|
||||
blending /= vec3(blending.x + blending.y + blending.z);
|
||||
return blending;
|
||||
if (N.z < (1.0 - limit) * (N.y + N.x)) {
|
||||
weight.z = 0.0;
|
||||
weight.y = 1.0 - weight.x;
|
||||
}
|
||||
else if (N.x < (1.0 - limit) * (N.y + N.z)) {
|
||||
weight.x = 0.0;
|
||||
weight.z = 1.0 - weight.y;
|
||||
}
|
||||
else if (N.y < (1.0 - limit) * (N.x + N.z)) {
|
||||
weight.y = 0.0;
|
||||
weight.x = 1.0 - weight.z;
|
||||
}
|
||||
else {
|
||||
weight = ((2.0 - limit) * N + (limit - 1.0)) / max(1e-8, clamp(blend, 0.0, 1.0));
|
||||
}
|
||||
|
||||
return color1 * weight.x + color2 * weight.y + color3 * weight.z;
|
||||
}
|
||||
|
||||
vec3 triplanarMapping (sampler2D ImageTexture, vec3 normal, vec3 position) {
|
||||
vec3 normalBlend = blendNormal(normal);
|
||||
vec3 xColor = texture(ImageTexture, position.yz).rgb;
|
||||
vec3 yColor = texture(ImageTexture, position.xz).rgb;
|
||||
vec3 zColor = texture(ImageTexture, position.xy).rgb;
|
||||
|
||||
return (xColor * normalBlend.x + yColor * normalBlend.y + zColor * normalBlend.z);
|
||||
vec2 sphericalMapping(vec3 coord) {
|
||||
vec3 vin = coord * 2.0 - vec3(1.0);
|
||||
float len = length(vin);
|
||||
float v, u;
|
||||
if (len > 0.0) {
|
||||
if (vin.x == 0.0 && vin.y == 0.0) {
|
||||
u = 0.0;
|
||||
}
|
||||
else {
|
||||
u = (1.0 - atan(vin.x, vin.y) / PI) * 0.5;
|
||||
}
|
||||
v = acos(clamp(vin.z / len, -1.0, 1.0)) / PI;
|
||||
}
|
||||
else {
|
||||
v = u = 0.0;
|
||||
}
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
vec2 tubeMapping(vec3 coord) {
|
||||
vec3 vin = coord * 2.0 - vec3(1.0);
|
||||
float u, v;
|
||||
v = - (vin.z + 1.0) * 0.5;
|
||||
float len = sqrt(vin.x * vin.x + vin.y * vin.y);
|
||||
if (len > 0.0) {
|
||||
u = (1.0 - (atan(vin.x / len, vin.y / len) / PI)) * 0.5;
|
||||
}
|
||||
else {
|
||||
v = u = 0.0;
|
||||
}
|
||||
return vec2(u, v);
|
||||
}
|
||||
|
||||
@ -8,13 +8,25 @@ float hash(const vec2 p) {
|
||||
}
|
||||
|
||||
vec2 envMapEquirect(const vec3 normal) {
|
||||
const float PI = 3.1415926535;
|
||||
const float PI2 = PI * 2.0;
|
||||
#ifndef PI
|
||||
#define PI 3.1415926535
|
||||
#endif
|
||||
#ifndef PI2
|
||||
#define PI2 6.2831853071
|
||||
#endif
|
||||
float phi = acos(normal.z);
|
||||
float theta = atan(-normal.y, normal.x) + PI;
|
||||
return vec2(theta / PI2, phi / PI);
|
||||
}
|
||||
|
||||
vec2 envMapMirror(const vec3 co) {
|
||||
vec3 nco = normalize(co);
|
||||
nco.y -= 1.0;
|
||||
float div = 2.0 * sqrt(max(-0.5 * nco.y, 0.0));
|
||||
nco /= max(1e-8, div);
|
||||
return 0.5 * nco.xz + 0.5;
|
||||
}
|
||||
|
||||
float rand(const vec2 co) { // Unreliable
|
||||
return fract(sin(dot(co.xy, vec2(12.9898, 78.233))) * 43758.5453);
|
||||
}
|
||||
|
||||
@ -4,6 +4,36 @@ uniform vec2 morphScaleOffset;
|
||||
uniform vec2 morphDataDim;
|
||||
uniform vec4 morphWeights[8];
|
||||
|
||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A, vec4 imorph) {
|
||||
vec3 morph = texture(morphDataPos, uvCoord).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.x * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.y * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - 2.0 * morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.z * morph;
|
||||
|
||||
morph = texture(morphDataPos, vec2(uvCoord.x, uvCoord.y - 3.0 * morphDataDim.y)).rgb * morphScaleOffset.x + morphScaleOffset.y;
|
||||
A += imorph.w * morph;
|
||||
}
|
||||
|
||||
void getMorphedNormal(vec2 uvCoord, vec3 oldNor, inout vec3 morphNor, vec4 imorph) {
|
||||
vec3 norm = oldNor + imorph.x * (texture(morphDataNor, uvCoord).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.y * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.z * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - 2.0 * morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
norm = oldNor + imorph.w * (texture(morphDataNor, vec2(uvCoord.x, uvCoord.y - 3.0 * morphDataDim.y)).rgb * 2.0 - 1.0);
|
||||
morphNor += norm;
|
||||
|
||||
morphNor = normalize(morphNor);
|
||||
}
|
||||
|
||||
void getMorphedVertex(vec2 uvCoord, inout vec3 A){
|
||||
vec3 totalDelta = vec3(0.0);
|
||||
for(int i = 0; i<8; i++ )
|
||||
|
||||
@ -22,6 +22,14 @@ uniform vec2 smSizeUniform;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform vec4 tileBoundsSunArray[maxLights * shadowmapCascades];
|
||||
#if defined(_Clusters) && defined(_Spot) && defined(_ShadowMap)
|
||||
uniform vec4 tileBoundsSpotArray[maxLightsCluster];
|
||||
#endif
|
||||
vec4 tileBounds = vec4(0.0, 0.0, 1.0, 1.0);
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// PCF that clamps samples to tile boundaries to prevent bleeding
|
||||
vec3 PCFTileAware(sampler2DShadow shadowMap,
|
||||
@ -67,9 +75,15 @@ vec3 PCFTileAware(sampler2DShadow shadowMap,
|
||||
|
||||
#ifdef _ShadowMapTransparent
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
vec3 transResult = vec3(0.0);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
vec4 smt = texture(shadowMapTransparent,
|
||||
clamp(uv + vec2(x, y) / smSize, tileMin, tileMax));
|
||||
transResult += (smt.a < compare) ? smt.rgb : vec3(1.0);
|
||||
}
|
||||
}
|
||||
result *= transResult / 9.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -134,9 +148,15 @@ vec3 PCF(sampler2DShadow shadowMap,
|
||||
|
||||
#ifdef _ShadowMapTransparent
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapTransparent, uv);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
vec3 transResult = vec3(0.0);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
vec4 smt = texture(shadowMapTransparent,
|
||||
uv + vec2(x, y) / smSize);
|
||||
transResult += (smt.a < compare) ? smt.rgb : vec3(1.0);
|
||||
}
|
||||
}
|
||||
result *= transResult / 9.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -179,9 +199,19 @@ vec3 PCFCube(samplerCubeShadow shadowMapCube,
|
||||
|
||||
#ifdef _ShadowMapTransparent
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapCubeTransparent, ml);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
vec3 transResult = vec3(0.0);
|
||||
vec4 smt = texture(shadowMapCubeTransparent, ml);
|
||||
transResult += (smt.a < compare) ? smt.rgb : vec3(1.0);
|
||||
for (int x = -1; x <= 1; x += 2) {
|
||||
for (int y = -1; y <= 1; y += 2) {
|
||||
for (int z = -1; z <= 1; z += 2) {
|
||||
smt = texture(shadowMapCubeTransparent,
|
||||
ml + vec3(x, y, z) * s);
|
||||
transResult += (smt.a < compare) ? smt.rgb : vec3(1.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
result *= transResult / 9.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -291,13 +321,13 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
|
||||
, const bool transparent
|
||||
#endif
|
||||
) {
|
||||
const vec2 smSize = smSizeUniform; // TODO: incorrect...
|
||||
const float compare = lpToDepth(lp, lightProj) - bias * 1.5;
|
||||
ml = ml + n * bias * 20;
|
||||
int faceIndex = 0;
|
||||
const int lightIndex = index * 6;
|
||||
const vec2 uv = sampleCube(ml, faceIndex);
|
||||
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||
const vec2 smSize = smSizeUniform; // TODO: incorrect...
|
||||
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
@ -377,9 +407,15 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
|
||||
|
||||
#ifdef _ShadowMapTransparent
|
||||
if (transparent == false) {
|
||||
vec4 shadowmap_transparent = texture(shadowMapTransparent, uvtiled);
|
||||
if (shadowmap_transparent.a < compare)
|
||||
result *= shadowmap_transparent.rgb;
|
||||
vec3 transResult = vec3(0.0);
|
||||
for (int x = -1; x <= 1; x++) {
|
||||
for (int y = -1; y <= 1; y++) {
|
||||
vec4 smt = texture(shadowMapTransparent,
|
||||
clamp(uvtiled + vec2(x, y) / smSize, 0.0, 1.0));
|
||||
transResult += (smt.a < compare) ? smt.rgb : vec3(1.0);
|
||||
}
|
||||
}
|
||||
result *= transResult / 9.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -387,10 +423,6 @@ vec3 PCFFakeCube(sampler2DShadow shadowMap,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform vec4 tileBounds;
|
||||
#endif
|
||||
|
||||
vec3 shadowTest(sampler2DShadow shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
sampler2D shadowMapTransparent,
|
||||
@ -405,9 +437,9 @@ vec3 shadowTest(sampler2DShadow shadowMap,
|
||||
#ifdef _ShadowMapAtlas
|
||||
// use tile PCF
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSizeAtlas = smSizeUniform;
|
||||
vec2 smSizeAtlas = smSizeUniform * (tileBounds.zw - tileBounds.xy);
|
||||
#else
|
||||
const vec2 smSizeAtlas = shadowmapSize;
|
||||
vec2 smSizeAtlas = shadowmapSize * (tileBounds.zw - tileBounds.xy);
|
||||
#endif
|
||||
return PCFTileAware(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
@ -455,7 +487,7 @@ mat4 getCascadeMat(const float d, out int casi, out int casIndex) {
|
||||
float(d > casData[c * 4].y),
|
||||
float(d > casData[c * 4].z),
|
||||
float(d > casData[c * 4].w));
|
||||
casi = int(min(dot(ci, comp), c));
|
||||
casi = int(min(dot(ci, comp), float(c - 1)));
|
||||
// Get cascade mat
|
||||
casIndex = casi * 4;
|
||||
return mat4(
|
||||
@ -479,8 +511,12 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
||||
#ifdef _SMSizeUniform
|
||||
vec2 smSize = smSizeUniform;
|
||||
#else
|
||||
#ifdef _ShadowMapAtlas
|
||||
vec2 smSize = shadowmapSize * (tileBoundsSunArray[0].zw - tileBoundsSunArray[0].xy);
|
||||
#else
|
||||
const vec2 smSize = shadowmapSize * vec2(shadowmapCascades, 1.0);
|
||||
#endif
|
||||
#endif
|
||||
const int c = shadowmapCascades;
|
||||
float d = distance(eye, p);
|
||||
int casi;
|
||||
@ -489,16 +525,35 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
||||
vec4 lPos = LWVP * vec4(p, 1.0);
|
||||
lPos.xyz /= lPos.w;
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSunArray[casi];
|
||||
#endif
|
||||
|
||||
vec3 visibility = vec3(1.0);
|
||||
if (lPos.w > 0.0) visibility = PCF(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos.xy, lPos.z - shadowsBias, smSize
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
if (lPos.w > 0.0) {
|
||||
#ifdef _ShadowMapAtlas
|
||||
visibility = PCFTileAware(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos.xy, lPos.z - shadowsBias, smSize,
|
||||
tileBounds.xy, tileBounds.zw
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
visibility = PCF(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos.xy, lPos.z - shadowsBias, smSize
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Blend cascade
|
||||
// https://github.com/TheRealMJP/Shadows
|
||||
@ -518,15 +573,33 @@ vec3 shadowTestCascade(sampler2DShadow shadowMap,
|
||||
lPos2.xyz /= lPos2.w;
|
||||
vec3 visibility2 = vec3(1.0);
|
||||
// use lPos2 coordinates for second cascade, not lPos
|
||||
if (lPos2.w > 0.0) visibility2 = PCF(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos2.xy, lPos2.z - shadowsBias, smSize
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#ifdef _ShadowMapAtlas
|
||||
tileBounds = tileBoundsSunArray[casi + 1];
|
||||
#endif
|
||||
if (lPos2.w > 0.0) {
|
||||
#ifdef _ShadowMapAtlas
|
||||
visibility2 = PCFTileAware(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos2.xy, lPos2.z - shadowsBias, smSize,
|
||||
tileBounds.xy, tileBounds.zw
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#else
|
||||
visibility2 = PCF(shadowMap,
|
||||
#ifdef _ShadowMapTransparent
|
||||
shadowMapTransparent,
|
||||
#endif
|
||||
lPos2.xy, lPos2.z - shadowsBias, smSize
|
||||
#ifdef _ShadowMapTransparent
|
||||
, transparent
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
float lerpAmt = smoothstep(0.0, blendThres, splitDist);
|
||||
return mix(visibility2, visibility, lerpAmt);
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
/* Various sky functions
|
||||
* =====================
|
||||
*
|
||||
* Nishita model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
* Single scattering model is based on https://github.com/wwwtyro/glsl-atmosphere (Unlicense License)
|
||||
*
|
||||
* Changes to the original implementation:
|
||||
* - r and pSun parameters of nishita_atmosphere() are already normalized
|
||||
* - Some original parameters of nishita_atmosphere() are replaced with pre-defined values
|
||||
* - r and pSun parameters of single_scatter_atmosphere() are already normalized
|
||||
* - Some original parameters of single_scatter_atmosphere() are replaced with pre-defined values
|
||||
* - Implemented air, dust and ozone density node parameters (see Blender source)
|
||||
* - Replaced the inner integral calculation with a LUT lookup
|
||||
*
|
||||
@ -22,43 +22,43 @@
|
||||
|
||||
#include "std/math.glsl"
|
||||
|
||||
uniform sampler2D nishitaLUT;
|
||||
uniform vec2 nishitaDensity;
|
||||
uniform sampler2D singleScatterLUT;
|
||||
uniform vec2 skyDensity;
|
||||
|
||||
#ifndef PI
|
||||
#define PI 3.141592
|
||||
#define PI 3.1415926535
|
||||
#endif
|
||||
#ifndef HALF_PI
|
||||
#define HALF_PI 1.570796
|
||||
#endif
|
||||
|
||||
#define nishita_iSteps 16
|
||||
#define single_scatter_iSteps 16
|
||||
|
||||
// These values are taken from Cycles code if they
|
||||
// exist there, otherwise they are taken from the example
|
||||
// in the glsl-atmosphere repo
|
||||
#define nishita_sun_intensity 22.0
|
||||
#define nishita_atmo_radius 6420e3
|
||||
#define nishita_rayleigh_scale 8e3
|
||||
#define nishita_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define nishita_mie_scale 1.2e3
|
||||
#define nishita_mie_coeff 2e-5
|
||||
#define nishita_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define nishita_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
#define single_scatter_sun_intensity 22.0
|
||||
#define single_scatter_atmo_radius 6420e3
|
||||
#define single_scatter_rayleigh_scale 8e3
|
||||
#define single_scatter_rayleigh_coeff vec3(5.5e-6, 13.0e-6, 22.4e-6)
|
||||
#define single_scatter_mie_scale 1.2e3
|
||||
#define single_scatter_mie_coeff 2e-5
|
||||
#define single_scatter_mie_dir 0.76 // Aerosols anisotropy ("direction")
|
||||
#define single_scatter_mie_dir_sq 0.5776 // Squared aerosols anisotropy
|
||||
|
||||
// Values from [Hill: 60]
|
||||
#define sun_limb_darkening_col vec3(0.397, 0.503, 0.652)
|
||||
|
||||
vec3 nishita_lookupLUT(const float height, const float sunTheta) {
|
||||
vec3 single_scatter_lookupLUT(const float height, const float sunTheta) {
|
||||
vec2 coords = vec2(
|
||||
sqrt(height * (1 / nishita_atmo_radius)),
|
||||
sqrt(height * (1 / single_scatter_atmo_radius)),
|
||||
0.5 + 0.5 * sign(sunTheta - HALF_PI) * sqrt(abs(sunTheta * (1 / HALF_PI) - 1))
|
||||
);
|
||||
return textureLod(nishitaLUT, coords, 0.0).rgb;
|
||||
return textureLod(singleScatterLUT, coords, 0.0).rgb;
|
||||
}
|
||||
|
||||
/* See raySphereIntersection() in leenkx/Sources/renderpath/Nishita.hx */
|
||||
vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
/* See raySphereIntersection() in leenkx/Sources/renderpath/Sky.hx */
|
||||
vec2 single_scatter_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
float a = dot(rd, rd);
|
||||
float b = 2.0 * dot(rd, r0);
|
||||
float c = dot(r0, r0) - (sr * sr);
|
||||
@ -74,12 +74,12 @@ vec2 nishita_rsi(const vec3 r0, const vec3 rd, const float sr) {
|
||||
* pSun: normalized sun direction
|
||||
* rPlanet: planet radius
|
||||
*/
|
||||
vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
vec3 single_scatter_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const float rPlanet) {
|
||||
// Calculate the step size of the primary ray
|
||||
vec2 p = nishita_rsi(r0, r, nishita_atmo_radius);
|
||||
vec2 p = single_scatter_rsi(r0, r, single_scatter_atmo_radius);
|
||||
if (p.x > p.y) return vec3(0.0);
|
||||
p.y = min(p.y, nishita_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(nishita_iSteps);
|
||||
p.y = min(p.y, single_scatter_rsi(r0, r, rPlanet).x);
|
||||
float iStepSize = (p.y - p.x) / float(single_scatter_iSteps);
|
||||
|
||||
// Primary ray time
|
||||
float iTime = 0.0;
|
||||
@ -96,18 +96,18 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
float mu = dot(r, pSun);
|
||||
float mumu = mu * mu;
|
||||
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - nishita_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + nishita_mie_dir_sq - 2.0 * mu * nishita_mie_dir, 1.5) * (2.0 + nishita_mie_dir_sq));
|
||||
float pMie = 3.0 / (8.0 * PI) * ((1.0 - single_scatter_mie_dir_sq) * (mumu + 1.0)) / (pow(1.0 + single_scatter_mie_dir_sq - 2.0 * mu * single_scatter_mie_dir, 1.5) * (2.0 + single_scatter_mie_dir_sq));
|
||||
|
||||
// Sample the primary ray
|
||||
for (int i = 0; i < nishita_iSteps; i++) {
|
||||
for (int i = 0; i < single_scatter_iSteps; i++) {
|
||||
|
||||
// Calculate the primary ray sample position and height
|
||||
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
|
||||
float iHeight = length(iPos) - rPlanet;
|
||||
|
||||
// Calculate the optical depth of the Rayleigh and Mie scattering for this step
|
||||
float odStepRlh = exp(-iHeight / nishita_rayleigh_scale) * nishitaDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / nishita_mie_scale) * nishitaDensity.y * iStepSize;
|
||||
float odStepRlh = exp(-iHeight / single_scatter_rayleigh_scale) * skyDensity.x * iStepSize;
|
||||
float odStepMie = exp(-iHeight / single_scatter_mie_scale) * skyDensity.y * iStepSize;
|
||||
|
||||
// Accumulate optical depth
|
||||
iOdRlh += odStepRlh;
|
||||
@ -116,12 +116,12 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
// Idea behind this: "Rotate" everything by iPos (-> iPos is the new zenith) and then all calculations for the
|
||||
// inner integral only depend on the sample height (iHeight) and sunTheta (angle between sun and new zenith).
|
||||
float sunTheta = safe_acos(dot(normalize(iPos), normalize(pSun)));
|
||||
vec3 jAttn = nishita_lookupLUT(iHeight, sunTheta);
|
||||
vec3 jAttn = single_scatter_lookupLUT(iHeight, sunTheta);
|
||||
|
||||
// Calculate attenuation
|
||||
vec3 iAttn = exp(-(
|
||||
nishita_mie_coeff * iOdMie
|
||||
+ nishita_rayleigh_coeff * iOdRlh
|
||||
single_scatter_mie_coeff * iOdMie
|
||||
+ single_scatter_rayleigh_coeff * iOdRlh
|
||||
// + 0 for ozone
|
||||
));
|
||||
vec3 attn = iAttn * jAttn;
|
||||
@ -136,7 +136,7 @@ vec3 nishita_atmosphere(const vec3 r, const vec3 r0, const vec3 pSun, const floa
|
||||
iTime += iStepSize;
|
||||
}
|
||||
|
||||
return nishita_sun_intensity * (pRlh * nishita_rayleigh_coeff * totalRlh + pMie * nishita_mie_coeff * totalMie);
|
||||
return single_scatter_sun_intensity * (pRlh * single_scatter_rayleigh_coeff * totalRlh + pMie * single_scatter_mie_coeff * totalMie);
|
||||
}
|
||||
|
||||
vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const float intensity) {
|
||||
@ -149,7 +149,83 @@ vec3 sun_disk(const vec3 n, const vec3 light_dir, const float disk_size, const f
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
return 1 + (1.0 - step(1.0, dist)) * nishita_sun_intensity * intensity * limb_darkening;
|
||||
return 1 + (1.0 - step(1.0, dist)) * single_scatter_sun_intensity * intensity * limb_darkening;
|
||||
}
|
||||
|
||||
uniform sampler2D multiScatterLUT;
|
||||
uniform vec4 multiScatterParams; // x=elevation, y=rotation, z=angular_diameter, w=intensity
|
||||
uniform vec4 multiScatterSunBottom; // xyz=sun_bottom, w=earth_intersection_angle
|
||||
uniform vec3 multiScatterSunTop;
|
||||
|
||||
// XYZ to sRGB/Rec.709 conversion (D65 white point)
|
||||
vec3 xyz_to_rgb(vec3 xyz) {
|
||||
return vec3(
|
||||
3.2406 * xyz.x - 1.5372 * xyz.y - 0.4986 * xyz.z,
|
||||
-0.9689 * xyz.x + 1.8758 * xyz.y + 0.0415 * xyz.z,
|
||||
0.0557 * xyz.x - 0.2040 * xyz.y + 1.0570 * xyz.z
|
||||
);
|
||||
}
|
||||
|
||||
float sky_elevation_to_v(float elevation) {
|
||||
float abs_el = abs(elevation);
|
||||
float l = sign(elevation) * sqrt(abs_el / 1.5707963);
|
||||
float v = (l + 1.0) * 0.5;
|
||||
return clamp(v, 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 multi_scatter_sample_lut(vec3 dir, float sun_rotation) {
|
||||
float azimuth = atan(dir.x, dir.y);
|
||||
float elevation = asin(clamp(dir.z, -1.0, 1.0));
|
||||
|
||||
azimuth -= sun_rotation;
|
||||
float u = fract(azimuth / (2.0 * PI));
|
||||
float v = sky_elevation_to_v(elevation);
|
||||
|
||||
return textureLod(multiScatterLUT, vec2(u, v), 0.0).rgb;
|
||||
}
|
||||
|
||||
vec3 multi_scatter_sun_disc(vec3 dir, vec3 sun_dir, float angular_diameter, float intensity) {
|
||||
float half_diameter = max(angular_diameter * 0.5, 0.0005);
|
||||
float dist = distance(dir, sun_dir) / half_diameter;
|
||||
float edge = smoothstep(1.0, 0.95, dist);
|
||||
if (edge <= 0.0) return vec3(0.0);
|
||||
|
||||
float horizon = multiScatterSunBottom.w;
|
||||
float dir_elev = asin(clamp(dir.z, -1.0, 1.0));
|
||||
float horizon_fade = smoothstep(horizon - half_diameter * 0.1, horizon + half_diameter * 0.1, dir_elev);
|
||||
edge *= horizon_fade;
|
||||
if (edge <= 0.0) return vec3(0.0);
|
||||
|
||||
float invDist = 1.0 - dist;
|
||||
float mu = sqrt(invDist * invDist);
|
||||
vec3 limb_darkening = 1.0 - (1.0 - pow(vec3(mu), sun_limb_darkening_col));
|
||||
|
||||
float sun_elev = multiScatterParams.x;
|
||||
float t = clamp((dir_elev - (sun_elev - half_diameter)) / (2.0 * half_diameter), 0.0, 1.0);
|
||||
vec3 sun_color = mix(multiScatterSunBottom.rgb, multiScatterSunTop, t) * intensity * limb_darkening;
|
||||
|
||||
return xyz_to_rgb(sun_color) * edge;
|
||||
}
|
||||
|
||||
vec3 multi_scatter_atmosphere(vec3 dir) {
|
||||
float sun_elevation = multiScatterParams.x;
|
||||
float sun_rotation = multiScatterParams.y;
|
||||
float angular_diameter = multiScatterParams.z;
|
||||
float sun_intensity = multiScatterParams.w;
|
||||
|
||||
vec3 xyz = multi_scatter_sample_lut(dir, sun_rotation);
|
||||
vec3 radiance = xyz_to_rgb(xyz);
|
||||
|
||||
if (sun_intensity > 0.0) {
|
||||
vec3 computed_sun_dir = vec3(
|
||||
sin(sun_rotation) * cos(sun_elevation),
|
||||
cos(sun_rotation) * cos(sun_elevation),
|
||||
sin(sun_elevation)
|
||||
);
|
||||
radiance += multi_scatter_sun_disc(dir, computed_sun_dir, angular_diameter, sun_intensity);
|
||||
}
|
||||
|
||||
return radiance;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
|
||||
// Separable SSS Transmittance Function, ref to sss_pass
|
||||
vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap) {
|
||||
const float translucency = 1.0;
|
||||
vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sampler2DShadow shadowMap, vec3 sssColor, float sssRadius
|
||||
#ifdef _ShadowMapAtlas
|
||||
, vec4 tileBounds
|
||||
#endif
|
||||
) {
|
||||
const float translucency = 0.85;
|
||||
vec4 shrinkedPos = vec4(p - 0.005 * n, 1.0);
|
||||
vec4 shadowPos = LWVP * shrinkedPos;
|
||||
float scale = 8.25 * (1.0 - translucency) / (sssWidth / 10.0);
|
||||
float d1 = texture(shadowMap, vec3(shadowPos.xy / shadowPos.w, shadowPos.z)).r; // 'd1' has a range of 0..1
|
||||
float d2 = shadowPos.z; // 'd2' has a range of 0..'lightFarPlane'
|
||||
d1 *= lightFar; // So we scale 'd1' accordingly:
|
||||
float d = scale * abs(d1 - d2);
|
||||
vec2 shadowUV = shadowPos.xy / shadowPos.w;
|
||||
#ifdef _ShadowMapAtlas
|
||||
shadowUV = clamp(shadowUV, tileBounds.xy, tileBounds.zw);
|
||||
#endif
|
||||
float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001);
|
||||
float d1 = texture(shadowMap, vec3(shadowUV, shadowPos.z)).r;
|
||||
float d2 = shadowPos.z;
|
||||
d1 *= lightFar;
|
||||
d2 *= lightFar;
|
||||
float d = scale * abs(d1 - d2) * 1000.0;
|
||||
|
||||
if (d > 10.0) return vec3(0.0);
|
||||
float dd = -d * d;
|
||||
vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
|
||||
vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
|
||||
@ -17,10 +27,70 @@ vec3 SSSSTransmittance(mat4 LWVP, vec3 p, vec3 n, vec3 l, float lightFar, sample
|
||||
vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
|
||||
vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
|
||||
vec3(0.078, 0.0, 0.0) * exp(dd / 7.41);
|
||||
return profile * clamp(0.3 + dot(l, -n), 0.0, 1.0);
|
||||
profile *= mix(vec3(1.0), sssColor, 0.8);
|
||||
return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0);
|
||||
}
|
||||
|
||||
vec3 SSSSTransmittanceCube(float translucency, vec4 shadowPos, vec3 n, vec3 l, float lightFar) {
|
||||
// TODO
|
||||
return vec3(0.0);
|
||||
#ifdef _ShadowMapAtlas
|
||||
vec3 SSSSTransmittanceCubeAtlas(sampler2DShadow shadowMap, vec3 lightPos, vec3 p, vec3 n, vec3 l, float lightFar, vec2 lightProj, int index, vec3 sssColor, float sssRadius) {
|
||||
const float translucency = 0.85;
|
||||
vec3 shrinkedPos = p - 0.005 * n;
|
||||
vec3 ld = normalize(shrinkedPos - lightPos);
|
||||
#ifdef _InvY
|
||||
ld.y = -ld.y;
|
||||
#endif
|
||||
float d2 = lpToDepth(ld, lightProj);
|
||||
int faceIndex = 0;
|
||||
int lightIndex = index * 6;
|
||||
vec2 uv = sampleCube(ld, faceIndex);
|
||||
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex];
|
||||
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y;
|
||||
#endif
|
||||
float d1 = texture(shadowMap, vec3(uvtiled, d2)).r;
|
||||
d1 *= lightFar;
|
||||
d2 *= lightFar;
|
||||
float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001);
|
||||
// d1/d2 are in meters, sssRadius is in mm, exponential constants are in mm^2
|
||||
float d = scale * abs(d1 - d2) * 1000.0; // Convert distance to mm
|
||||
|
||||
if (d > 10.0) return vec3(0.0);
|
||||
float dd = -d * d;
|
||||
vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
|
||||
vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
|
||||
vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) +
|
||||
vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
|
||||
vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
|
||||
vec3(0.078, 0.0, 0.0) * exp(dd / 7.41);
|
||||
profile *= mix(vec3(1.0), sssColor, 0.8);
|
||||
return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 SSSSTransmittanceCube(samplerCubeShadow shadowMapCube, vec3 lightPos, vec3 p, vec3 n, vec3 l, float lightFar, vec2 lightProj, vec3 sssColor, float sssRadius) {
|
||||
const float translucency = 0.85;
|
||||
vec3 shrinkedPos = p - 0.005 * n;
|
||||
vec3 ld = normalize(shrinkedPos - lightPos);
|
||||
#ifdef _InvY
|
||||
ld.y = -ld.y;
|
||||
#endif
|
||||
float d2 = lpToDepth(ld, lightProj);
|
||||
float d1 = texture(shadowMapCube, vec4(ld, d2)).r;
|
||||
d1 *= lightFar;
|
||||
d2 *= lightFar;
|
||||
float scale = 2.5 * (1.0 - translucency) / max(sssRadius, 0.001);
|
||||
// d1/d2 are in meters, sssRadius is in mm, exponential constants are in mm^2
|
||||
float d = scale * abs(d1 - d2) * 1000.0; // Convert distance to mm
|
||||
|
||||
if (d > 10.0) return vec3(0.0);
|
||||
float dd = -d * d;
|
||||
vec3 profile = vec3(0.233, 0.455, 0.649) * exp(dd / 0.0064) +
|
||||
vec3(0.1, 0.336, 0.344) * exp(dd / 0.0484) +
|
||||
vec3(0.118, 0.198, 0.0) * exp(dd / 0.187) +
|
||||
vec3(0.113, 0.007, 0.007) * exp(dd / 0.567) +
|
||||
vec3(0.358, 0.004, 0.0) * exp(dd / 1.99) +
|
||||
vec3(0.078, 0.0, 0.0) * exp(dd / 7.41);
|
||||
profile *= mix(vec3(1.0), sssColor, 0.8);
|
||||
return profile * clamp(0.5 + dot(l, -n), 0.0, 1.0);
|
||||
}
|
||||
|
||||
@ -1,136 +0,0 @@
|
||||
#version 450
|
||||
|
||||
layout (local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
|
||||
|
||||
#include "compiled.inc"
|
||||
#include "std/math.glsl"
|
||||
#include "std/gbuffer.glsl"
|
||||
#include "std/imageatomic.glsl"
|
||||
#ifdef _VoxelShadow
|
||||
#include "std/conetrace.glsl"
|
||||
#endif
|
||||
|
||||
uniform vec3 lightPos;
|
||||
uniform vec3 lightColor;
|
||||
uniform int lightType;
|
||||
uniform vec3 lightDir;
|
||||
uniform vec2 spotData;
|
||||
#ifdef _ShadowMap
|
||||
uniform int lightShadow;
|
||||
uniform vec2 lightProj;
|
||||
uniform float shadowsBias;
|
||||
uniform mat4 LVP;
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform int index;
|
||||
uniform vec4 pointLightDataArray[maxLightsCluster * 6];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
uniform float clipmaps[voxelgiClipmapCount * 10];
|
||||
uniform int clipmapLevel;
|
||||
|
||||
uniform layout(r32ui) uimage3D voxelsLight;
|
||||
|
||||
#ifdef _ShadowMap
|
||||
uniform sampler2DShadow shadowMap;
|
||||
uniform sampler2D shadowMapTransparent;
|
||||
uniform sampler2DShadow shadowMapSpot;
|
||||
#ifdef _ShadowMapAtlas
|
||||
uniform sampler2DShadow shadowMapPoint;
|
||||
#else
|
||||
uniform samplerCubeShadow shadowMapPoint;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _ShadowMapAtlas
|
||||
// https://www.khronos.org/registry/OpenGL/specs/gl/glspec20.pdf // p:168
|
||||
// https://www.gamedev.net/forums/topic/687535-implementing-a-cube-map-lookup-function/5337472/
|
||||
vec2 sampleCube(vec3 dir, out int faceIndex) {
|
||||
vec3 dirAbs = abs(dir);
|
||||
float ma;
|
||||
vec2 uv;
|
||||
if(dirAbs.z >= dirAbs.x && dirAbs.z >= dirAbs.y) {
|
||||
faceIndex = dir.z < 0.0 ? 5 : 4;
|
||||
ma = 0.5 / dirAbs.z;
|
||||
uv = vec2(dir.z < 0.0 ? -dir.x : dir.x, -dir.y);
|
||||
}
|
||||
else if(dirAbs.y >= dirAbs.x) {
|
||||
faceIndex = dir.y < 0.0 ? 3 : 2;
|
||||
ma = 0.5 / dirAbs.y;
|
||||
uv = vec2(dir.x, dir.y < 0.0 ? -dir.z : dir.z);
|
||||
}
|
||||
else {
|
||||
faceIndex = dir.x < 0.0 ? 1 : 0;
|
||||
ma = 0.5 / dirAbs.x;
|
||||
uv = vec2(dir.x < 0.0 ? dir.z : -dir.z, -dir.y);
|
||||
}
|
||||
// downscale uv a little to hide seams
|
||||
// transform coordinates from clip space to texture space
|
||||
#ifndef _FlipY
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
#ifdef HLSL
|
||||
return uv * 0.9976 * ma + 0.5;
|
||||
#else
|
||||
return vec2(uv.x * ma, uv.y * -ma) * 0.9976 + 0.5;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
float lpToDepth(vec3 lp, const vec2 lightProj) {
|
||||
lp = abs(lp);
|
||||
float zcomp = max(lp.x, max(lp.y, lp.z));
|
||||
zcomp = lightProj.x - lightProj.y / zcomp;
|
||||
return zcomp * 0.5 + 0.5;
|
||||
}
|
||||
|
||||
void main() {
|
||||
int res = voxelgiResolution.x;
|
||||
ivec3 dst = ivec3(gl_GlobalInvocationID.xyz);
|
||||
|
||||
vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x;
|
||||
wposition = wposition * 2.0 - 1.0;
|
||||
wposition *= float(clipmaps[int(clipmapLevel * 10)]);
|
||||
wposition *= voxelgiResolution.x;
|
||||
wposition += vec3(clipmaps[clipmapLevel * 10 + 4], clipmaps[clipmapLevel * 10 + 5], clipmaps[clipmapLevel * 10 + 6]);
|
||||
|
||||
float visibility;
|
||||
vec3 lp = lightPos - wposition;
|
||||
vec3 l;
|
||||
if (lightType == 0) { l = lightDir; visibility = 1.0; }
|
||||
else { l = normalize(lp); visibility = attenuate(distance(wposition, lightPos)); }
|
||||
|
||||
#ifdef _ShadowMap
|
||||
if (lightShadow == 1) {
|
||||
vec4 lightPosition = LVP * vec4(wposition, 1.0);
|
||||
vec3 lPos = lightPosition.xyz / lightPosition.w;
|
||||
visibility *= texture(shadowMap, vec3(lPos.xy, lPos.z - shadowsBias)).r;
|
||||
}
|
||||
else if (lightShadow == 2) {
|
||||
vec4 lightPosition = LVP * vec4(wposition, 1.0);
|
||||
vec3 lPos = lightPosition.xyz / lightPosition.w;
|
||||
visibility *= texture(shadowMapSpot, vec3(lPos.xy, lPos.z - shadowsBias)).r;
|
||||
}
|
||||
else if (lightShadow == 3) {
|
||||
#ifdef _ShadowMapAtlas
|
||||
int faceIndex = 0;
|
||||
const int lightIndex = index * 6;
|
||||
const vec2 uv = sampleCube(-l, faceIndex);
|
||||
vec4 pointLightTile = pointLightDataArray[lightIndex + faceIndex]; // x: tile X offset, y: tile Y offset, z: tile size relative to atlas
|
||||
vec2 uvtiled = pointLightTile.z * uv + pointLightTile.xy;
|
||||
#ifdef _FlipY
|
||||
uvtiled.y = 1.0 - uvtiled.y; // invert Y coordinates for direct3d coordinate system
|
||||
#endif
|
||||
visibility *= texture(shadowMapPoint, vec3(uvtiled, lpToDepth(lp, lightProj) - shadowsBias)).r;
|
||||
#else
|
||||
visibility *= texture(shadowMapPoint, vec4(-l, lpToDepth(lp, lightProj) - shadowsBias)).r;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
vec3 light = visibility * lightColor;
|
||||
imageAtomicAdd(voxelsLight, dst, uint(light.r * 255));
|
||||
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x), uint(light.g * 255));
|
||||
imageAtomicAdd(voxelsLight, dst + ivec3(0, 0, voxelgiResolution.x * 2), uint(light.b * 255));
|
||||
}
|
||||
@ -38,7 +38,6 @@ uniform layout(r8) image3D voxelsOut;
|
||||
#endif
|
||||
|
||||
uniform int clipmapLevel;
|
||||
uniform float voxelBlend;
|
||||
|
||||
uniform float clipmaps[voxelgiClipmapCount * 10];
|
||||
|
||||
@ -47,7 +46,7 @@ void main() {
|
||||
ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
|
||||
src.y += clipmapLevel * res;
|
||||
|
||||
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
|
||||
for (int i = 0; i < 6 + diffuseConeCount; i++)
|
||||
{
|
||||
vec4 col = vec4(0.0);
|
||||
|
||||
|
||||
@ -151,7 +151,7 @@ void main() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= albedo;
|
||||
envl.rgb *= diffuseIBL(albedo, roughness, f0, dotNV);
|
||||
|
||||
#ifdef _Brdf
|
||||
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
|
||||
@ -165,7 +165,7 @@ void main() {
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= envmapStrength * occspec.x;
|
||||
envl.rgb *= envmapStrength * voxelgiEnv * occspec.x;
|
||||
|
||||
vec3 occ = envl * (1.0 - traceAO(P, n, voxels, clipmaps));
|
||||
|
||||
|
||||
@ -53,13 +53,6 @@ uniform float shirr[7 * 4];
|
||||
#ifdef _Brdf
|
||||
uniform sampler2D senvmapBrdf;
|
||||
#endif
|
||||
#ifdef _Rad
|
||||
uniform sampler2D senvmapRadiance;
|
||||
uniform int envmapNumMipmaps;
|
||||
#endif
|
||||
#ifdef _EnvCol
|
||||
uniform vec3 backgroundCol;
|
||||
#endif
|
||||
|
||||
void main() {
|
||||
const vec2 pixel = gl_GlobalInvocationID.xy;
|
||||
@ -140,37 +133,20 @@ void main() {
|
||||
vec3 envl = vec3(0.0);
|
||||
#endif
|
||||
|
||||
#ifdef _Rad
|
||||
vec3 reflectionWorld = reflect(-v, n);
|
||||
float lod = getMipFromRoughness(roughness, envmapNumMipmaps);
|
||||
vec3 prefilteredColor = textureLod(senvmapRadiance, envMapEquirect(reflectionWorld), lod).rgb;
|
||||
#endif
|
||||
|
||||
#ifdef _EnvLDR
|
||||
envl.rgb = pow(envl.rgb, vec3(2.2));
|
||||
#ifdef _Rad
|
||||
prefilteredColor = pow(prefilteredColor, vec3(2.2));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= albedo;
|
||||
envl.rgb *= diffuseIBL(albedo, roughness, f0, dotNV);
|
||||
|
||||
#ifdef _Brdf
|
||||
envl.rgb *= 1.0 - F; //LV: We should take refracted light into account
|
||||
#endif
|
||||
|
||||
#ifdef _Rad // Indirect specular
|
||||
envl.rgb += prefilteredColor * F; //LV: Removed "1.5 * occspec.y". Specular should be weighted only by FV LUT
|
||||
#else
|
||||
#ifdef _EnvCol
|
||||
envl.rgb += backgroundCol * F; //LV: Eh, what's the point of weighting it only by F0?
|
||||
#endif
|
||||
#endif
|
||||
|
||||
envl.rgb *= envmapStrength * occspec.x;
|
||||
envl.rgb *= envmapStrength * voxelgiEnv * occspec.x;
|
||||
|
||||
vec4 trace = traceDiffuse(P, n, voxels, clipmaps);
|
||||
vec3 color = trace.rgb * albedo * (1.0 - F);
|
||||
vec3 color = trace.rgb * diffuseIBL(albedo, roughness, f0, dotNV) * (1.0 - F);
|
||||
color += envl * (1.0 - trace.a);
|
||||
|
||||
imageStore(voxels_diffuse, ivec2(pixel), vec4(color, 1.0));
|
||||
|
||||
@ -48,13 +48,15 @@ void main() {
|
||||
const vec2 pixel = gl_GlobalInvocationID.xy;
|
||||
vec2 uv = (pixel + 0.5) / postprocess_resolution;
|
||||
#ifdef _InvY
|
||||
uv.y = 1.0 - uv.y
|
||||
uv.y = 1.0 - uv.y;
|
||||
#endif
|
||||
|
||||
float depth = textureLod(gbufferD, uv, 0.0).r * 2.0 - 1.0;
|
||||
if (depth == 0) return;
|
||||
|
||||
vec2 ior_opac = textureLod(gbuffer_refraction, uv, 0.0).xy;
|
||||
float ior = unpackIOR(ior_opac.x);
|
||||
float opacity = ior_opac.y;
|
||||
|
||||
float x = uv.x * 2 - 1;
|
||||
float y = uv.y * 2 - 1;
|
||||
@ -72,8 +74,8 @@ void main() {
|
||||
n = normalize(n);
|
||||
|
||||
vec3 color = vec3(0.0);
|
||||
if(ior_opac.y < 1.0)
|
||||
color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior_opac.x, g0.b, clipmaps, pixel).rgb;
|
||||
if(opacity < 1.0)
|
||||
color = traceRefraction(P, n, voxels, voxelsSDF, normalize(eye - P), ior, g0.b, clipmaps, pixel).rgb;
|
||||
|
||||
imageStore(voxels_refraction, ivec2(pixel), vec4(color, 1.0));
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@ void main() {
|
||||
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
|
||||
n = normalize(n);
|
||||
|
||||
float occ = 1.0 - traceShadow(P, n, voxels, voxelsSDF, normalize(lPos - P), clipmaps, pixel);
|
||||
float occ = 1.0 - traceShadow(P, n, voxels, voxelsSDF, normalize(lPos - P), clipmaps, pixel, vec2(0.0));
|
||||
|
||||
imageStore(voxels_shadows, ivec2(pixel), vec4(occ));
|
||||
}
|
||||
|
||||
@ -66,9 +66,13 @@ void main() {
|
||||
n.xy = n.z >= 0.0 ? g0.xy : octahedronWrap(g0.xy);
|
||||
n = normalize(n);
|
||||
|
||||
float roughness = g0.b;
|
||||
|
||||
vec3 v = normalize(eye - P);
|
||||
|
||||
vec2 velocity = -textureLod(sveloc, uv, 0.0).rg;
|
||||
|
||||
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, normalize(eye - P), g0.z * g0.z, clipmaps, pixel, velocity).rgb;
|
||||
vec3 color = traceSpecular(P, n, voxels, voxelsSDF, v, roughness * roughness, clipmaps, pixel, velocity).rgb;
|
||||
|
||||
imageStore(voxels_specular, ivec2(pixel), vec4(color, 1.0));
|
||||
}
|
||||
|
||||
@ -43,7 +43,6 @@ uniform mat4 LVP;
|
||||
#endif
|
||||
uniform sampler3D voxelsSampler;
|
||||
uniform layout(r32ui) uimage3D voxels;
|
||||
uniform layout(r32ui) uimage3D voxelsLight;
|
||||
uniform layout(rgba8) image3D voxelsB;
|
||||
uniform layout(rgba8) image3D voxelsOut;
|
||||
uniform layout(r8) image3D SDF;
|
||||
@ -75,21 +74,13 @@ void main() {
|
||||
#endif
|
||||
|
||||
#ifdef _VoxelGI
|
||||
vec3 light = vec3(0.0);
|
||||
light.r = float(imageLoad(voxelsLight, ivec3(gl_GlobalInvocationID.xyz)).r) / 255;
|
||||
light.g = float(imageLoad(voxelsLight, ivec3(gl_GlobalInvocationID.xyz) + ivec3(0, 0, voxelgiResolution.x)).r) / 255;
|
||||
light.b = float(imageLoad(voxelsLight, ivec3(gl_GlobalInvocationID.xyz) + ivec3(0, 0, voxelgiResolution.x * 2)).r) / 255;
|
||||
light /= 3;
|
||||
vec4 aniso_colors[6];
|
||||
#else
|
||||
float aniso_colors[6];
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < 6 + DIFFUSE_CONE_COUNT; i++)
|
||||
for (int i = 0; i < 6 + diffuseConeCount; i++)
|
||||
{
|
||||
#ifdef _VoxelGI
|
||||
vec4 aniso_colors[6];
|
||||
#else
|
||||
float aniso_colors[6];
|
||||
#endif
|
||||
|
||||
ivec3 src = ivec3(gl_GlobalInvocationID.xyz);
|
||||
src.x += i * res;
|
||||
ivec3 dst = src;
|
||||
@ -103,30 +94,37 @@ void main() {
|
||||
|
||||
if (i < 6) {
|
||||
#ifdef _VoxelGI
|
||||
uint count = imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 15)).r;
|
||||
if (count > 0) {
|
||||
vec4 basecol = vec4(0.0);
|
||||
basecol.r = float(imageLoad(voxels, src)) / 255;
|
||||
basecol.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x))) / 255;
|
||||
basecol.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 2))) / 255;
|
||||
basecol.a = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 3))) / 255;
|
||||
basecol /= 4;
|
||||
basecol /= count;
|
||||
vec3 emission = vec3(0.0);
|
||||
emission.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 4))) / 255;
|
||||
emission.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 5))) / 255;
|
||||
emission.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 6))) / 255;
|
||||
emission /= 3;
|
||||
emission /= count;
|
||||
vec3 N = vec3(0.0);
|
||||
N.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 7))) / 255;
|
||||
N.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 8))) / 255;
|
||||
N /= 2;
|
||||
N /= count;
|
||||
vec3 wnormal = decode_oct(N.rg * 2 - 1);
|
||||
vec3 envl = vec3(0.0);
|
||||
envl.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 9))) / 255;
|
||||
envl.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 10))) / 255;
|
||||
envl.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 11))) / 255;
|
||||
envl /= 3;
|
||||
envl /= count;
|
||||
#ifdef _HOSEK
|
||||
envl *= 100;
|
||||
#endif
|
||||
vec3 light = vec3(0.0);
|
||||
light.r = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 12))) / 255;
|
||||
light.g = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 13))) / 255;
|
||||
light.b = float(imageLoad(voxels, src + ivec3(0, 0, voxelgiResolution.x * 14))) / 255;
|
||||
light /= count;
|
||||
|
||||
//clipmap to world
|
||||
vec3 wposition = (gl_GlobalInvocationID.xyz + 0.5) / voxelgiResolution.x;
|
||||
@ -138,8 +136,9 @@ void main() {
|
||||
radiance = basecol;
|
||||
vec4 trace = traceDiffuse(wposition, wnormal, voxelsSampler, clipmaps);
|
||||
vec3 indirect = trace.rgb + envl.rgb * (1.0 - trace.a);
|
||||
radiance.rgb *= light.rgb + indirect.rgb;
|
||||
radiance.rgb *= light.rgb * INV_PI + indirect.rgb;
|
||||
radiance.rgb += emission.rgb;
|
||||
}
|
||||
|
||||
#else
|
||||
opac = float(imageLoad(voxels, src)) / 255;
|
||||
@ -196,7 +195,7 @@ void main() {
|
||||
}
|
||||
else {
|
||||
// precompute cone sampling:
|
||||
vec3 coneDirection = DIFFUSE_CONE_DIRECTIONS[i - 6];
|
||||
vec3 coneDirection = diffuseConeDirections[i - 6];
|
||||
vec3 aniso_direction = -coneDirection;
|
||||
uvec3 face_offsets = uvec3(
|
||||
aniso_direction.x > 0 ? 0 : 1,
|
||||
|
||||
@ -88,58 +88,142 @@ vec4 rayCast(vec3 dir) {
|
||||
}
|
||||
#endif //SSR
|
||||
|
||||
vec3 sampleWaterNormals(vec2 hitXY, float speed, out vec2 tcnor0, out vec2 tcnor1) {
|
||||
tcnor0 = hitXY / 3.0;
|
||||
vec3 n0 = textureLod(sdetail, tcnor0 + vec2(speed / 60.0, speed / 120.0), 0.0).rgb;
|
||||
tcnor1 = hitXY / 6.0 + n0.xy / 20.0;
|
||||
vec3 n1 = textureLod(sbase, tcnor1 + vec2(speed / 40.0, speed / 80.0), 0.0).rgb;
|
||||
return normalize(n0 + n1 - 1.0);
|
||||
}
|
||||
|
||||
float waveHeight(vec2 xy, float speed) {
|
||||
return (sin(xy.x * 10.0 / waterDisplace + speed) * cos(xy.y * 10.0 / waterDisplace + speed) + sin(xy.x * 20.0 / waterDisplace + speed * 1.3) * cos(xy.y * 20.0 / waterDisplace + speed * 1.3) * 0.5) / 50.0 * waterDisplace;
|
||||
}
|
||||
|
||||
void main() {
|
||||
float gdepth = textureLod(gbufferD, texCoord, 0.0).r * 2.0 - 1.0;
|
||||
if (gdepth == 1.0) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
// Eye below water
|
||||
if (eye.z < waterLevel) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
// Displace surface
|
||||
vec3 vray = normalize(viewRay);
|
||||
vec3 p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||
float speed = time * 2.0 * waterSpeed;
|
||||
p.z += sin(p.x * 10.0 / waterDisplace + speed) * cos(p.y * 10.0 / waterDisplace + speed) / 50.0 * waterDisplace;
|
||||
float farPlane = cameraProj.y / (1.0 - cameraProj.x);
|
||||
float cosAng = dot(eyeLook, vray);
|
||||
float domeR = farPlane * 0.94;
|
||||
float tDome = -3.5 * vray.z + sqrt(max(domeR * domeR - 12.25 * (1.0 - vray.z * vray.z), 0.0));
|
||||
float domeDepth01 = cameraProj.x + cameraProj.y / (tDome * cosAng);
|
||||
bool isSky = (gdepth * 0.5 + 0.5) >= domeDepth01;
|
||||
|
||||
// Ray-plane intersection with water surface (z = waterLevel)
|
||||
float denom = vray.z;
|
||||
float tWater = (waterLevel - eye.z) / denom;
|
||||
bool hasWaterHit = (abs(denom) > 0.0001) && (tWater > 0.0);
|
||||
|
||||
if (eye.z < waterLevel) {
|
||||
vec2 tc = texCoord;
|
||||
float depthThick = 1.0 + (waterLevel - eye.z);
|
||||
vec3 p = vec3(0.0);
|
||||
float geomDist = farPlane;
|
||||
if (!isSky) {
|
||||
p = getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||
geomDist = length(p - eye);
|
||||
}
|
||||
bool upRay = hasWaterHit && denom > 0.0;
|
||||
float waterDist = upRay ? min(tWater, geomDist) : geomDist;
|
||||
|
||||
vec2 wxy;
|
||||
if (upRay && tWater < geomDist) {
|
||||
wxy = (eye + tWater * vray).xy;
|
||||
} else if (!isSky) {
|
||||
wxy = p.xy;
|
||||
} else {
|
||||
wxy = eye.xy + vray.xy * 50.0;
|
||||
}
|
||||
vec2 tc0, tc1;
|
||||
vec3 n2 = sampleWaterNormals(wxy * waterFreq, speed, tc0, tc1);
|
||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
float ddepth2 = textureLod(gbufferD, tc, 0.0).r * 2.0 - 1.0;
|
||||
float dist2 = cameraProj.y / ((ddepth2 * 0.5 + 0.5) - cameraProj.x) / cosAng;
|
||||
if (dist2 > geomDist + 1.0) tc = texCoord;
|
||||
|
||||
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
||||
vec3 outCol = refracted;
|
||||
|
||||
if (upRay && tWater < geomDist) {
|
||||
outCol *= max(1.0 - length(n2.xy) * 1.5, 0.0);
|
||||
vec3 h2 = normalize(-vray + ld);
|
||||
outCol += pow(max(dot(n2, h2), 0.0), 80.0) * 0.6;
|
||||
}
|
||||
|
||||
if (!isSky) {
|
||||
float fdw = abs(p.z - (waterLevel + waveHeight(p.xy, speed)));
|
||||
float bandw = 0.1 + waterDist * 0.003;
|
||||
if (fdw < bandw) {
|
||||
float foamLod = clamp(waterDist / 15.0, 0.0, 5.0);
|
||||
vec3 fm0 = textureLod(sfoam, tc0 * 3.0 + vec2(speed / 30.0, speed / 50.0), foamLod).rgb;
|
||||
vec3 fm1 = textureLod(sfoam, tc1 * 4.0 + vec2(-speed / 35.0, speed / 45.0), foamLod).rgb;
|
||||
float fstr = clamp(1.0 - fm0.r * 0.5 - fm1.b * 0.5, 0.0, 1.0);
|
||||
float ffac = clamp(1.0 - fdw / bandw, 0.0, 1.0);
|
||||
float foamFade = clamp(1.0 - (waterLevel - eye.z) * 0.4, 0.0, 1.0);
|
||||
vec3 foamCol = mix(waterColor, vec3(1.0), 0.6);
|
||||
outCol = mix(outCol, foamCol, ffac * fstr * 0.5 * foamFade);
|
||||
}
|
||||
}
|
||||
|
||||
float fogFactor = 1.0 - exp(-waterDist * waterDensity * 0.006 * depthThick);
|
||||
outCol = mix(outCol, waterColor, clamp(fogFactor, 0.0, 1.0));
|
||||
|
||||
fragColor.rgb = outCol;
|
||||
fragColor.a = 1.0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Above water
|
||||
if (p.z > waterLevel) {
|
||||
if (!hasWaterHit || denom >= 0.0) {
|
||||
fragColor = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSky) tWater = min(tWater, 100.0); // Clamp to prevent aliasing at horizon
|
||||
vec3 p = isSky ? (eye + tWater * vray) : getPos(eye, eyeLook, vray, gdepth, cameraProj);
|
||||
|
||||
if (!isSky && p.z > waterLevel - 0.01) {
|
||||
fragColor = textureLod(tex, texCoord, 0.0);
|
||||
return;
|
||||
}
|
||||
float horizonFactor = clamp(1.0 - tWater / 60.0, 0.0, 1.0);
|
||||
|
||||
// Displace surface
|
||||
float geomZ = p.z; // undisplaced depth for foam/fog tests
|
||||
p.z += waveHeight(p.xy, speed);
|
||||
|
||||
// Hit plane to determine uvs
|
||||
vec3 v = normalize(eye - p.xyz);
|
||||
float t = -(dot(eye, vec3(0.0, 0.0, 1.0)) - waterLevel) / dot(v, vec3(0.0, 0.0, 1.0));
|
||||
vec3 v = normalize(eye - p);
|
||||
float t = (waterLevel - eye.z) / v.z;
|
||||
vec3 hit = eye + t * v;
|
||||
hit.xy *= waterFreq;
|
||||
hit.z += waterLevel;
|
||||
|
||||
// Sample normal maps
|
||||
vec2 tcnor0 = hit.xy / 3.0;
|
||||
vec3 n0 = textureLod(sdetail, tcnor0 + vec2(speed / 60.0, speed / 120.0), 0.0).rgb;
|
||||
vec2 tcnor0, tcnor1;
|
||||
vec3 n2 = sampleWaterNormals(hit.xy * waterFreq, speed, tcnor0, tcnor1);
|
||||
|
||||
vec2 tcnor1 = hit.xy / 6.0 + n0.xy / 20.0;
|
||||
vec3 n1 = textureLod(sbase, tcnor1 + vec2(speed / 40.0, speed / 80.0), 0.0).rgb;
|
||||
vec3 n2 = normalize(((n1 + n0) / 2.0) * 2.0 - 1.0);
|
||||
|
||||
float ddepth = textureLod(gbufferD, texCoord + (n2.xy * n2.z) / 40.0, 0.0).r * 2.0 - 1.0;
|
||||
vec3 p2 = getPos(eye, eyeLook, vray, ddepth, cameraProj);
|
||||
vec2 tc = p2.z > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
// Refraction
|
||||
vec2 tc;
|
||||
if (isSky) {
|
||||
tc = texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
} else {
|
||||
float ddepth = textureLod(gbufferD, texCoord + (n2.xy * n2.z) / 40.0, 0.0).r * 2.0 - 1.0;
|
||||
float dist2 = cameraProj.y / ((ddepth * 0.5 + 0.5) - cameraProj.x) / cosAng;
|
||||
tc = (eye.z + vray.z * dist2) > waterLevel ? texCoord : texCoord + (n2.xy * n2.z) / 30.0 * waterRefract;
|
||||
}
|
||||
|
||||
// Light
|
||||
float fresnel = 1.0 - max(dot(n2, v), 0.0);
|
||||
fresnel = pow(fresnel, 30.0) * 0.45;
|
||||
fresnel = 0.02 + 0.98 * pow(fresnel, 5.0);
|
||||
vec3 r = reflect(-v, n2);
|
||||
#ifdef _Rad
|
||||
vec3 reflectedEnv = textureLod(senvmapRadiance, envMapEquirect(r), 0).rgb;
|
||||
vec3 reflectedEnv = textureLod(senvmapRadiance, envMapEquirect(r), 0).rgb;
|
||||
#else
|
||||
const vec3 reflectedEnv = vec3(0.5);
|
||||
#endif
|
||||
vec3 refracted = textureLod(tex, tc, 0.0).rgb;
|
||||
|
||||
|
||||
#ifdef _SSR
|
||||
float roughness = 0.1;//unpackFloat(g0.b).y;
|
||||
//if (roughness == 1.0) { fragColor.rgb = vec3(0.0); return; }
|
||||
@ -147,8 +231,8 @@ void main() {
|
||||
float spec = 0.9;//fract(textureLod(gbuffer1, texCoord, 0.0).a);
|
||||
//if (spec == 0.0) { fragColor.rgb = vec3(0.0); return; }
|
||||
|
||||
vec3 viewNormal = n2;
|
||||
vec3 viewPos = getPosView(viewRay, gdepth, cameraProj);
|
||||
vec3 viewNormal = V3 * n2;
|
||||
vec3 viewPos = isSky ? vec3(0.0) : getPosView(viewRay, gdepth, cameraProj);
|
||||
vec3 reflected = reflect(normalize(viewPos), viewNormal);
|
||||
hitCoord = viewPos;
|
||||
|
||||
@ -158,7 +242,6 @@ void main() {
|
||||
vec3 dir = reflected * (1.0 - rand(texCoord) * ssrJitter * roughness) * 2.0;
|
||||
#endif
|
||||
|
||||
// * max(ssrMinRayStep, -viewPos.z)
|
||||
vec4 coords = rayCast(dir);
|
||||
|
||||
vec2 deltaCoords = abs(vec2(0.5, 0.5) - coords.xy);
|
||||
@ -177,20 +260,37 @@ void main() {
|
||||
#else
|
||||
fragColor.rgb = mix(refracted, reflectedEnv, waterReflect * fresnel);
|
||||
#endif
|
||||
fragColor.rgb *= waterColor;
|
||||
fragColor.rgb += clamp(pow(max(dot(r, ld), 0.0), 200.0) * (200.0 + 8.0) / (PI * 8.0), 0.0, 2.0);
|
||||
fragColor.rgb *= 1.0 - (clamp(-(p.z - waterLevel) * waterDensity, 0.0, 0.9));
|
||||
fragColor.a = clamp(abs(p.z - waterLevel) * 5.0, 0.0, 1.0);
|
||||
|
||||
// Foam
|
||||
float fd = abs(p.z - waterLevel);
|
||||
if (fd < 0.1) {
|
||||
float colorMix = isSky ? 0.7 : 0.5;
|
||||
fragColor.rgb = mix(fragColor.rgb, waterColor, colorMix);
|
||||
// Blinn-Phong specular using half-vector, faded at horizon
|
||||
vec3 h = normalize(v + ld);
|
||||
float specAmount = pow(max(dot(n2, h), 0.0), 200.0) * (200.0 + 8.0) / (PI * 8.0);
|
||||
fragColor.rgb += specAmount * (isSky ? 0.3 : 1.0) * horizonFactor;
|
||||
float waterColumn = isSky ? 1.0 : max(length(p - eye) - tWater, 0.0);
|
||||
float depthFog = 1.0 - exp(-waterColumn * waterDensity * 0.006);
|
||||
vec3 surfaceCol = mix(waterColor, reflectedEnv, waterReflect * fresnel);
|
||||
fragColor.rgb = mix(fragColor.rgb, surfaceCol, min(depthFog, 0.9));
|
||||
// Alpha fades smoothly at horizon instead of hard cut
|
||||
// Skydome sits 3.5 below the camera (_skydomeMatrix), so its horizon
|
||||
// appears where the ray z reaches -3.5 / domeRadius
|
||||
float horizonFade = clamp((-3.5 / (farPlane * 0.95) - vray.z) * 20.0, 0.0, 1.0);
|
||||
fragColor.a = isSky ? horizonFade : clamp(abs(geomZ - waterLevel) * 5.0 + 0.1, 0.0, 1.0);
|
||||
float waveH = p.z - geomZ; // displacement already applied above
|
||||
float band = 0.1 + tWater * 0.003;
|
||||
float fd = isSky ? 1.0 : abs(geomZ - (waterLevel + waveH));
|
||||
if (fd < band) {
|
||||
// Based on foam by Owen Deery
|
||||
// http://fire-face.com/personal/water
|
||||
vec3 foamMask0 = textureLod(sfoam, tcnor0 * 10, 0.0).rgb;
|
||||
vec3 foamMask1 = textureLod(sfoam, tcnor1 * 11, 0.0).rgb;
|
||||
vec3 foam = vec3(1.0) - foamMask0.rrr - foamMask1.bbb;
|
||||
float fac = 1.0 - (fd * (1.0 / 0.1));
|
||||
fragColor.rgb = mix(fragColor.rgb, clamp(foam, 0.0, 1.0), clamp(fac, 0.0, 1.0));
|
||||
// Distance-based LOD blurs foam at range to reduce noise
|
||||
float foamLod = clamp(tWater / 15.0, 0.0, 5.0);
|
||||
vec2 foamUV0 = tcnor0 * 3.0 + vec2(speed / 30.0, speed / 50.0);
|
||||
vec2 foamUV1 = tcnor1 * 4.0 + vec2(-speed / 35.0, speed / 45.0);
|
||||
vec3 foamMask0 = textureLod(sfoam, foamUV0, foamLod).rgb;
|
||||
vec3 foamMask1 = textureLod(sfoam, foamUV1, foamLod).rgb;
|
||||
float foamMask = clamp(1.0 - foamMask0.r * 0.5 - foamMask1.b * 0.5, 0.0, 1.0);
|
||||
float fac = clamp(1.0 - fd / band, 0.0, 1.0);
|
||||
vec3 foamCol = mix(waterColor, vec3(1.0), 0.6);
|
||||
fragColor.rgb = mix(fragColor.rgb, foamCol, fac * foamMask * 0.8);
|
||||
fragColor.a = max(fragColor.a, fac * foamMask);
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
},
|
||||
{
|
||||
"name": "ld",
|
||||
"link": "_lightDirection"
|
||||
"link": "_sunDirection"
|
||||
},
|
||||
{
|
||||
"name": "invVP",
|
||||
|
||||
@ -237,8 +237,24 @@ class App {
|
||||
traitRenders.remove(f);
|
||||
}
|
||||
|
||||
public static function notifyOnRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
traitRenders2D.push(f);
|
||||
public static function notifyOnRender2D(f: kha.graphics2.Graphics->Void, index: Int = -1) {
|
||||
if (index < 0 || index >= traitRenders2D.length) {
|
||||
traitRenders2D.push(f);
|
||||
} else {
|
||||
traitRenders2D.insert(index, f);
|
||||
}
|
||||
}
|
||||
|
||||
public static function moveRender2D(f: kha.graphics2.Graphics->Void, newIndex: Int) {
|
||||
var oldIndex = traitRenders2D.indexOf(f);
|
||||
if (oldIndex != -1) {
|
||||
traitRenders2D.splice(oldIndex, 1);
|
||||
if (newIndex >= traitRenders2D.length) {
|
||||
traitRenders2D.push(f);
|
||||
} else {
|
||||
traitRenders2D.insert(newIndex, f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function removeRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
|
||||
@ -18,6 +18,13 @@ import iron.object.LightObject;
|
||||
import iron.object.MeshObject;
|
||||
import iron.object.Uniforms;
|
||||
import iron.object.Clipmap;
|
||||
#if lnx_radix
|
||||
import haxe.io.FPHelper;
|
||||
import radix.Radix;
|
||||
#end
|
||||
#if lnx_occlusion_culling
|
||||
import iron.culling.CullSystem;
|
||||
#end
|
||||
#if lnx_vr
|
||||
import iron.math.Vec4;
|
||||
import iron.math.Mat4;
|
||||
@ -34,9 +41,9 @@ class RenderPath {
|
||||
public static var vrCalibrationPosition:Vec4 = null;
|
||||
public static var vrCalibrationRotation:iron.math.Quat = null;
|
||||
public static var vrCalibrationSaved:Bool = false;
|
||||
|
||||
|
||||
public static var vrCenterCameraWorld:Mat4 = null;
|
||||
|
||||
|
||||
static var vrOriginalSuperSample:Float = -1.0;
|
||||
public static inline function isVRPresenting(): Bool {
|
||||
#if (kha_webgl && lnx_vr)
|
||||
@ -45,15 +52,11 @@ class RenderPath {
|
||||
return false;
|
||||
#end
|
||||
}
|
||||
|
||||
|
||||
public static inline function isVRSimulateMode(): Bool {
|
||||
return vrSimulateMode;
|
||||
}
|
||||
|
||||
// TODO: done remove safely
|
||||
public static inline function debugLog(msg: String, once: Bool = true): Void {
|
||||
return;
|
||||
}
|
||||
#end
|
||||
|
||||
public var frameScissor = false;
|
||||
@ -81,6 +84,18 @@ class RenderPath {
|
||||
var renderToXRFramebuffer = false;
|
||||
#end
|
||||
public var drawOrder = DrawOrder.Distance;
|
||||
#if lnx_frustum_culling
|
||||
public var frustumCulling = true;
|
||||
#else
|
||||
public var frustumCulling = false;
|
||||
#end
|
||||
|
||||
#if lnx_occlusion_culling
|
||||
public var occlusionCulling = true;
|
||||
#else
|
||||
public var occlusionCulling = false;
|
||||
#end
|
||||
|
||||
public var paused = false;
|
||||
public var ready(get, never): Bool;
|
||||
function get_ready(): Bool { return loading == 0; }
|
||||
@ -95,6 +110,8 @@ class RenderPath {
|
||||
var lastH = 0;
|
||||
var bindParams: Array<String>;
|
||||
var meshesSorted: Bool;
|
||||
var cullComputed: Bool;
|
||||
var cullFlagsReset: Bool;
|
||||
var scissorSet = false;
|
||||
var viewportScaled = false;
|
||||
var lastFrameTime = 0.0;
|
||||
@ -162,24 +179,24 @@ class RenderPath {
|
||||
|
||||
var appW = iron.App.w();
|
||||
var appH = iron.App.h();
|
||||
|
||||
|
||||
// use native XR framebuffer dimensions
|
||||
#if (kha_webgl && lnx_vr)
|
||||
if (kha.vr.VrInterface.instance != null) {
|
||||
var vr = kha.vr.VrInterface.instance;
|
||||
var isPresenting = vr != null && vr.IsPresenting();
|
||||
|
||||
|
||||
// save/restore camera position between modes
|
||||
if (!wasVRPresenting && isPresenting) {
|
||||
if (Scene.active != null && Scene.active.camera != null) {
|
||||
if (vrCalibrationPosition == null) vrCalibrationPosition = new Vec4();
|
||||
if (vrCalibrationRotation == null) vrCalibrationRotation = new Quat();
|
||||
|
||||
|
||||
vrCalibrationPosition.setFrom(Scene.active.camera.transform.loc);
|
||||
vrCalibrationRotation.setFrom(Scene.active.camera.transform.rot);
|
||||
vrCalibrationSaved = true;
|
||||
}
|
||||
|
||||
|
||||
// save original super sampling for later
|
||||
vrOriginalSuperSample = leenkx.renderpath.Inc.superSample;
|
||||
|
||||
@ -196,18 +213,18 @@ class RenderPath {
|
||||
#if (kha_webgl && lnx_vr)
|
||||
iron.system.Time.vrFrameTime = -1.0;
|
||||
#end
|
||||
|
||||
|
||||
if (vrCalibrationSaved && Scene.active != null && Scene.active.camera != null) {
|
||||
Scene.active.camera.transform.loc.setFrom(vrCalibrationPosition);
|
||||
Scene.active.camera.transform.rot.setFrom(vrCalibrationRotation);
|
||||
Scene.active.camera.buildMatrix();
|
||||
Scene.active.camera.buildProjection();
|
||||
}
|
||||
|
||||
|
||||
// restore original super sampling from simulate mode
|
||||
if (vrOriginalSuperSample >= 0.0) {
|
||||
leenkx.renderpath.Inc.superSample = vrOriginalSuperSample;
|
||||
|
||||
|
||||
for (rt in renderTargets) {
|
||||
if (rt.raw.width == 0 && rt.raw.scale != null) {
|
||||
rt.raw.scale = vrOriginalSuperSample;
|
||||
@ -216,13 +233,13 @@ class RenderPath {
|
||||
resize();
|
||||
vrOriginalSuperSample = -1.0;
|
||||
}
|
||||
|
||||
|
||||
// reset offset for next session
|
||||
vrCameraOffsetSet = false;
|
||||
vrCameraOffset = null;
|
||||
}
|
||||
wasVRPresenting = isPresenting;
|
||||
|
||||
|
||||
if (isPresenting) {
|
||||
// TODO: re-investigate using super sampling to avoid pixelation in simulate mode while giving max quality in headset
|
||||
if (vrOriginalSuperSample >= 0.0 && leenkx.renderpath.Inc.superSample != 4.0) {
|
||||
@ -234,7 +251,7 @@ class RenderPath {
|
||||
}
|
||||
resize();
|
||||
}
|
||||
|
||||
|
||||
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||
if (xrVr.xrGLLayer != null) {
|
||||
appW = xrVr.xrGLLayer.framebufferWidth;
|
||||
@ -304,10 +321,15 @@ class RenderPath {
|
||||
currentD = 1;
|
||||
currentFace = -1;
|
||||
meshesSorted = false;
|
||||
cullComputed = false;
|
||||
cullFlagsReset = false;
|
||||
sun = null;
|
||||
|
||||
for (l in Scene.active.lights) {
|
||||
if (l.visible) l.buildMatrix(Scene.active.camera);
|
||||
if (l.data.raw.type == "sun") sun = l;
|
||||
if (l.data.raw.type == "sun") {
|
||||
if (sun == null || (!sun.data.raw.cast_shadow && l.data.raw.cast_shadow)) sun = l;
|
||||
}
|
||||
else point = l;
|
||||
}
|
||||
light = Scene.active.lights[0];
|
||||
@ -381,7 +403,7 @@ class RenderPath {
|
||||
if (currentG != null) end();
|
||||
currentG = g;
|
||||
additionalTargets = additionalRenderTargets;
|
||||
|
||||
|
||||
// we still bind but skip begin() when explicitly rendering to XR framebuffer (renderToXRFramebuffer flag)
|
||||
#if lnx_vr
|
||||
if (!renderToXRFramebuffer) {
|
||||
@ -402,7 +424,7 @@ class RenderPath {
|
||||
currentG.disableScissor();
|
||||
scissorSet = false;
|
||||
}
|
||||
|
||||
|
||||
#if lnx_vr
|
||||
if (beginCalled) {
|
||||
currentG.end();
|
||||
@ -467,6 +489,60 @@ class RenderPath {
|
||||
rt.image.generateMipmaps(1000);
|
||||
}
|
||||
|
||||
#if lnx_radix
|
||||
static var sortDistanceKeys = new Array<Int>();
|
||||
static var sortDistanceMeshesBuf = new Array<MeshObject>();
|
||||
static var sortDistanceKeysBuf = new Array<Int>();
|
||||
|
||||
static var sortIndexKeys = new Array<Int>();
|
||||
static var sortIndexMeshesBuf = new Array<MeshObject>();
|
||||
static var sortIndexKeysBuf = new Array<Int>();
|
||||
|
||||
public static function sortMeshesDistance(meshes: Array<MeshObject>) {
|
||||
var n = meshes.length;
|
||||
var keys = sortDistanceKeys;
|
||||
var meshesBuf = sortDistanceMeshesBuf;
|
||||
var keysBuf = sortDistanceKeysBuf;
|
||||
if (keys.length < n) {
|
||||
keys.resize(n);
|
||||
meshesBuf.resize(n);
|
||||
keysBuf.resize(n);
|
||||
}
|
||||
for (i in 0...n) {
|
||||
var m = meshes[i];
|
||||
var key = FPHelper.floatToI32(m.cameraDistance) >>> 1;
|
||||
#if rp_depth_texture
|
||||
if (m.depthRead) key |= 0x80000000;
|
||||
#end
|
||||
keys[i] = key;
|
||||
}
|
||||
Radix.sortCopyKeyed(meshes, keys, meshesBuf, keysBuf);
|
||||
if (meshesBuf.length > n) meshesBuf.resize(n);
|
||||
}
|
||||
|
||||
public static function sortMeshesIndex(meshes: Array<MeshObject>) {
|
||||
var n = meshes.length;
|
||||
var keys = sortIndexKeys;
|
||||
var meshesBuf = sortIndexMeshesBuf;
|
||||
var keysBuf = sortIndexKeysBuf;
|
||||
if (keys.length < n) {
|
||||
keys.resize(n);
|
||||
meshesBuf.resize(n);
|
||||
keysBuf.resize(n);
|
||||
}
|
||||
for (i in 0...n) {
|
||||
var m = meshes[i];
|
||||
var si = m.data.sortingIndex;
|
||||
var key = si < -0x40000000 ? 0 : si > 0x3fffffff ? 0x7fffffff : si + 0x40000000;
|
||||
#if rp_depth_texture
|
||||
if (m.depthRead) key |= 0x80000000;
|
||||
#end
|
||||
keys[i] = key;
|
||||
}
|
||||
Radix.sortCopyKeyed(meshes, keys, meshesBuf, keysBuf);
|
||||
if (meshesBuf.length > n) meshesBuf.resize(n);
|
||||
}
|
||||
#else
|
||||
static inline function boolToInt(b: Bool): Int {
|
||||
return b ? 1 : 0;
|
||||
}
|
||||
@ -498,9 +574,10 @@ class RenderPath {
|
||||
return a.data.name > b.data.name ? 1 : -1;
|
||||
});
|
||||
}
|
||||
#end
|
||||
|
||||
public function drawMeshes(context: String) {
|
||||
var isShadows = context == "shadowmap";
|
||||
var isShadows = context == "shadowmap" || context == "shadowmap_transparent";
|
||||
if (isShadows) {
|
||||
// Disabled shadow casting for this light
|
||||
if (light == null || !light.data.raw.cast_shadow || !light.visible || light.data.raw.strength == 0) return;
|
||||
@ -516,6 +593,11 @@ class RenderPath {
|
||||
for (i in 0...LightObject.cascadeCount) {
|
||||
light.setCascade(Scene.active.camera, i);
|
||||
currentG.viewport(i * step, 0, step, step);
|
||||
#if lnx_occlusion_culling
|
||||
if (occlusionCulling && CullSystem.active != null) {
|
||||
CullSystem.active.cullShadow(light);
|
||||
}
|
||||
#end
|
||||
submitDraw(context);
|
||||
}
|
||||
drawn = true;
|
||||
@ -526,15 +608,20 @@ class RenderPath {
|
||||
if (context == "mesh") LightObject.updateClusters(Scene.active.camera);
|
||||
#end
|
||||
|
||||
if (!drawn) submitDraw(context);
|
||||
if (!drawn) {
|
||||
#if lnx_occlusion_culling
|
||||
if (isShadows && occlusionCulling && CullSystem.active != null) {
|
||||
CullSystem.active.cullShadow(light);
|
||||
}
|
||||
#end
|
||||
submitDraw(context);
|
||||
}
|
||||
|
||||
#if lnx_debug
|
||||
// Callbacks to specific context
|
||||
if (contextEvents != null) {
|
||||
var ar = contextEvents.get(context);
|
||||
if (ar != null) for (i in 0...ar.length) ar[i](currentG, i, ar.length);
|
||||
}
|
||||
#end
|
||||
|
||||
end();
|
||||
}
|
||||
@ -545,7 +632,34 @@ class RenderPath {
|
||||
var meshes = Scene.active.meshes;
|
||||
MeshObject.lastPipeline = null;
|
||||
|
||||
if (!meshesSorted && camera != null) { // Order max once per frame for now
|
||||
#if lnx_occlusion_culling
|
||||
if (!cullFlagsReset && !occlusionCulling) {
|
||||
cullFlagsReset = true;
|
||||
for (m in meshes) {
|
||||
m.occlusionVisible = true;
|
||||
m.shadowOcclusionVisible = true;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
if (!cullComputed && (context == "mesh" || context == "depth")) {
|
||||
#if lnx_occlusion_culling
|
||||
if (occlusionCulling) {
|
||||
if (CullSystem.active == null) CullSystem.init();
|
||||
if (CullSystem.active != null && camera != null) {
|
||||
if (CullSystem.active.proxiesDirty) {
|
||||
CullSystem.active.loadOccluderProxies();
|
||||
CullSystem.active.proxiesDirty = false;
|
||||
}
|
||||
CullSystem.active.cull(camera);
|
||||
}
|
||||
}
|
||||
#end
|
||||
cullComputed = true;
|
||||
}
|
||||
|
||||
var isShadowCtx = context == "shadowmap" || context == "shadowmap_transparent";
|
||||
if (!meshesSorted && camera != null && !isShadowCtx) {
|
||||
var camX = camera.transform.worldx();
|
||||
var camY = camera.transform.worldy();
|
||||
var camZ = camera.transform.worldz();
|
||||
@ -570,8 +684,20 @@ class RenderPath {
|
||||
|
||||
static inline function meshRenderLoop(g: Graphics, context: String, _bindParams: Array<String>, _meshes: Array<MeshObject>) {
|
||||
var isReadingDepth = false;
|
||||
#if lnx_occlusion_culling
|
||||
var cullSys = CullSystem.active;
|
||||
#end
|
||||
|
||||
for (m in _meshes) {
|
||||
#if lnx_occlusion_culling
|
||||
if (cullSys != null && !m.occlusionVisible && (context == "mesh" || context == "depth" || context == "translucent")) {
|
||||
continue;
|
||||
}
|
||||
if (cullSys != null && !m.shadowOcclusionVisible && (context == "shadowmap" || context == "shadowmap_transparent")) {
|
||||
continue;
|
||||
}
|
||||
#end
|
||||
|
||||
#if rp_depth_texture
|
||||
// First mesh that reads depth
|
||||
if (!isReadingDepth && m.depthRead) {
|
||||
@ -594,7 +720,6 @@ class RenderPath {
|
||||
}
|
||||
}
|
||||
|
||||
#if lnx_debug
|
||||
static var contextEvents: Map<String, Array<Graphics->Int->Int->Void>> = null;
|
||||
public static function notifyOnContext(name: String, onContext: Graphics->Int->Int->Void) {
|
||||
if (contextEvents == null) contextEvents = new Map();
|
||||
@ -605,7 +730,13 @@ class RenderPath {
|
||||
}
|
||||
ar.push(onContext);
|
||||
}
|
||||
#end
|
||||
|
||||
public static function removeNotifyOnContext(name: String, onContext: Graphics->Int->Int->Void) {
|
||||
if (contextEvents != null) {
|
||||
var ar = contextEvents.get(name);
|
||||
if (ar != null) ar.remove(onContext);
|
||||
}
|
||||
}
|
||||
|
||||
#if rp_decals
|
||||
public function drawDecals(context: String) {
|
||||
@ -672,7 +803,7 @@ class RenderPath {
|
||||
}
|
||||
|
||||
#if (rp_voxels != "Off")
|
||||
public function getComputeShader(handle: String): kha.compute.Shader {
|
||||
public function getComputeShader(handle: String): kha.graphics4.ComputeShader {
|
||||
return Reflect.field(kha.Shaders, handle + "_comp");
|
||||
}
|
||||
#end
|
||||
@ -686,7 +817,7 @@ class RenderPath {
|
||||
if (vr == null || vr._glContext == null || vr.xrGLLayer == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var gl: js.html.webgl.WebGL2RenderingContext = cast vr._glContext;
|
||||
var source = renderTargets.get(sourceTarget);
|
||||
if (source == null) {
|
||||
@ -697,21 +828,21 @@ class RenderPath {
|
||||
return;
|
||||
}
|
||||
// trace('Framebuffer OK');
|
||||
|
||||
|
||||
renderToXRFramebuffer = true;
|
||||
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER, vr.xrGLLayer.framebuffer);
|
||||
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER, sourceFB);
|
||||
|
||||
|
||||
var readStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.READ_FRAMEBUFFER);
|
||||
var drawStatus = gl.checkFramebufferStatus(js.html.webgl.WebGL2RenderingContext.DRAW_FRAMEBUFFER);
|
||||
if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE ||
|
||||
if (readStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE ||
|
||||
drawStatus != js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER_COMPLETE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var halfWidth = Std.int(source.image.width / 2);
|
||||
var fullHeight = source.image.height;
|
||||
|
||||
|
||||
if (vr._leftViewport != null) {
|
||||
var vp = vr._leftViewport;
|
||||
gl.blitFramebuffer(
|
||||
@ -730,7 +861,7 @@ class RenderPath {
|
||||
js.html.webgl.WebGL2RenderingContext.LINEAR
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
gl.bindFramebuffer(js.html.webgl.WebGL2RenderingContext.FRAMEBUFFER, null);
|
||||
renderToXRFramebuffer = false;
|
||||
#end
|
||||
@ -743,24 +874,24 @@ class RenderPath {
|
||||
if (currentG == null && frameG != null) {
|
||||
currentG = frameG;
|
||||
}
|
||||
|
||||
|
||||
var appw = iron.App.w();
|
||||
var apph = iron.App.h();
|
||||
var g = currentG;
|
||||
|
||||
|
||||
// get render target dimensions not App.w/h gbuffer is scaled in simulate mode with supersampling
|
||||
|
||||
var gbuffer0 = renderTargets.get("gbuffer0");
|
||||
var actualWidth = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.width : appw;
|
||||
var actualHeight = (gbuffer0 != null && gbuffer0.image != null) ? gbuffer0.image.height : apph;
|
||||
var actualHalfWidth = Std.int(actualWidth / 2);
|
||||
|
||||
|
||||
var vrFBWidth = actualWidth;
|
||||
var vrFBHeight = actualHeight;
|
||||
var vrHalfWidth = actualHalfWidth;
|
||||
var isVRPresenting = false;
|
||||
vrSimulateMode = false;
|
||||
|
||||
|
||||
var vr:Dynamic = null;
|
||||
var vrExists = false;
|
||||
#if (kha_webgl && lnx_vr)
|
||||
@ -773,7 +904,7 @@ class RenderPath {
|
||||
if (vrExists && vr != null && vr.IsPresenting()) {
|
||||
vrSimulateMode = false;
|
||||
isVRPresenting = true;
|
||||
|
||||
|
||||
// get framebuffer dimensions from XR layer
|
||||
#if (kha_webgl && lnx_vr)
|
||||
var xrVr: kha.js.vr.VrInterface = cast vr;
|
||||
@ -783,16 +914,16 @@ class RenderPath {
|
||||
vrHalfWidth = Std.int(vrFBWidth / 2);
|
||||
}
|
||||
#end
|
||||
|
||||
|
||||
if (Scene.active == null || Scene.active.camera == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
#if (kha_webgl && lnx_vr)
|
||||
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||
#end
|
||||
|
||||
|
||||
// LEFT EYE
|
||||
// HMD center for room scale position tracking
|
||||
#if (kha_webgl && lnx_vr)
|
||||
@ -802,10 +933,10 @@ class RenderPath {
|
||||
if (viewerTransform != null && viewerTransform.position != null) {
|
||||
// VR present calibration is used to position objects in world space not the camera
|
||||
var pos = viewerTransform.position;
|
||||
|
||||
|
||||
// camera follows headset directly in local floor space
|
||||
Scene.active.camera.transform.loc.set(pos.x, pos.y, pos.z);
|
||||
|
||||
|
||||
if (viewerTransform.orientation != null) {
|
||||
Scene.active.camera.transform.rot.set(
|
||||
viewerTransform.orientation.x,
|
||||
@ -819,7 +950,7 @@ class RenderPath {
|
||||
}
|
||||
iron.system.VRController.updatePoses();
|
||||
#end
|
||||
|
||||
|
||||
Scene.active.camera.V.self = vr.GetViewMatrix(0);
|
||||
Scene.active.camera.P.self = vr.GetProjectionMatrix(0);
|
||||
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||
@ -829,7 +960,7 @@ class RenderPath {
|
||||
var renderWidth = actualWidth;
|
||||
var renderHeight = actualHeight;
|
||||
var renderHalfWidth = actualHalfWidth;
|
||||
|
||||
|
||||
// left half of render target
|
||||
g.viewport(0, 0, renderHalfWidth, renderHeight);
|
||||
g.scissor(0, 0, renderHalfWidth, renderHeight);
|
||||
@ -841,12 +972,12 @@ class RenderPath {
|
||||
Scene.active.camera.VP.setFrom(Scene.active.camera.P);
|
||||
Scene.active.camera.VP.multmat(Scene.active.camera.V);
|
||||
Scene.active.camera.buildMatrix();
|
||||
|
||||
|
||||
// right half of render target
|
||||
g.viewport(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||
g.scissor(renderHalfWidth, 0, renderHalfWidth, renderHeight);
|
||||
drawMeshes();
|
||||
|
||||
|
||||
// restore for post-processing
|
||||
g.disableScissor();
|
||||
g.viewport(0, 0, renderWidth, renderHeight);
|
||||
@ -859,7 +990,7 @@ class RenderPath {
|
||||
if (vrCenterCameraWorld == null) vrCenterCameraWorld = Mat4.identity();
|
||||
vrCenterCameraWorld.setFrom(Scene.active.camera.transform.world);
|
||||
#end
|
||||
|
||||
|
||||
Scene.active.camera.buildProjection(actualHalfWidth / actualHeight);
|
||||
|
||||
Scene.active.camera.transform.move(Scene.active.camera.right(), -ipd_offset);
|
||||
@ -902,7 +1033,9 @@ class RenderPath {
|
||||
#end
|
||||
|
||||
Data.getShader(shaderPath[0], shaderPath[1], function(res: ShaderData) {
|
||||
cc.context = res.getContext(shaderPath[2]);
|
||||
if (res != null) {
|
||||
cc.context = res.getContext(shaderPath[2]);
|
||||
}
|
||||
loading--;
|
||||
});
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import iron.object.SpeakerObject;
|
||||
import iron.object.DecalObject;
|
||||
import iron.object.ProbeObject;
|
||||
import iron.object.Tilesheet;
|
||||
import iron.object.CurveObject;
|
||||
import iron.data.CameraData;
|
||||
import iron.data.MeshData;
|
||||
import iron.data.LightData;
|
||||
@ -64,6 +65,7 @@ class Scene {
|
||||
#end
|
||||
public var empties: Array<Object>;
|
||||
public var animations: Array<Animation>;
|
||||
public var tilesheets: Array<Tilesheet>;
|
||||
#if lnx_skin
|
||||
public var armatures: Array<Armature>;
|
||||
#end
|
||||
@ -71,6 +73,13 @@ class Scene {
|
||||
|
||||
public var embedded: Map<String, kha.Image>;
|
||||
|
||||
#if (rp_renderer == "Deferred")
|
||||
public static inline var MAX_MATERIALS = 16;
|
||||
public static inline var FLOATS_PER_MATERIAL_PARAM = 32; // 8 vec4s per material
|
||||
public var materialParamsBuffer: kha.arrays.Float32Array;
|
||||
public var materialParamsDirty: Bool = true;
|
||||
#end
|
||||
|
||||
public var ready: Bool; // Async in progress
|
||||
|
||||
public var traitInits: Array<Void->Void> = [];
|
||||
@ -103,10 +112,14 @@ class Scene {
|
||||
#end
|
||||
empties = [];
|
||||
animations = [];
|
||||
tilesheets = [];
|
||||
#if lnx_skin
|
||||
armatures = [];
|
||||
#end
|
||||
embedded = new Map();
|
||||
#if (rp_renderer == "Deferred")
|
||||
materialParamsBuffer = new kha.arrays.Float32Array(MAX_MATERIALS * FLOATS_PER_MATERIAL_PARAM);
|
||||
#end
|
||||
root = new Object();
|
||||
root.name = "Root";
|
||||
traitInits = [];
|
||||
@ -125,6 +138,14 @@ class Scene {
|
||||
|
||||
// Startup scene
|
||||
active.addScene(format.name, null, function(sceneObject: Object) {
|
||||
|
||||
if (format.properties != null) {
|
||||
sceneObject.properties = new Map();
|
||||
for (p in format.properties) {
|
||||
sceneObject.properties.set(p.name, cleanValue(p.value));
|
||||
}
|
||||
}
|
||||
|
||||
// Create traits bottom-up (children first, then parents)
|
||||
createTraitsBottomUp(sceneObject);
|
||||
|
||||
@ -204,6 +225,89 @@ class Scene {
|
||||
root.remove();
|
||||
}
|
||||
|
||||
#if (rp_renderer == "Deferred")
|
||||
public function markMaterialParamsDirty() {
|
||||
materialParamsDirty = true;
|
||||
}
|
||||
|
||||
public function updateMaterialParams() {
|
||||
if (!materialParamsDirty) return;
|
||||
materialParamsDirty = false;
|
||||
var buf = materialParamsBuffer;
|
||||
for (m in meshes) {
|
||||
if (m.materials == null) continue;
|
||||
for (mat in m.materials) {
|
||||
if (mat == null) continue;
|
||||
if (mat.contexts == null) continue;
|
||||
var slot = -1;
|
||||
var bc = null;
|
||||
for (ctx in mat.contexts) {
|
||||
if (ctx == null || ctx.raw == null) continue;
|
||||
if (ctx.raw.name == "mesh" && ctx.raw.bind_constants != null) {
|
||||
bc = ctx.raw.bind_constants;
|
||||
for (c in bc) {
|
||||
if (c != null && c.name == "materialID" && c.intValue != null) {
|
||||
slot = c.intValue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot < 0 || slot >= MAX_MATERIALS) continue;
|
||||
var base = slot * FLOATS_PER_MATERIAL_PARAM;
|
||||
if (base + FLOATS_PER_MATERIAL_PARAM - 1 >= buf.length) continue;
|
||||
if (bc == null) continue;
|
||||
for (i in 0...FLOATS_PER_MATERIAL_PARAM) buf[base + i] = 0.0;
|
||||
buf[base + 6] = 1.5; // coatIOR
|
||||
buf[base + 12] = 1.45; // ior
|
||||
buf[base + 13] = 1.0; // thinWall
|
||||
for (c in bc) {
|
||||
if (c == null || c.name == null || c.floatValue == null) continue;
|
||||
switch (c.name) {
|
||||
// matp0: vec4(anisotropy, anisoRot, sheen, sheenRough)
|
||||
case "anisotropy": buf[base + 0] = c.floatValue;
|
||||
case "anisoRot": buf[base + 1] = c.floatValue;
|
||||
case "sheen": buf[base + 2] = c.floatValue;
|
||||
case "sheenRough": buf[base + 3] = c.floatValue;
|
||||
// matp1: vec4(clearcoat, clearcoatRough, coatIOR, coatTintR)
|
||||
case "clearcoat": buf[base + 4] = c.floatValue;
|
||||
case "clearcoatRough": buf[base + 5] = c.floatValue;
|
||||
case "coatIOR": buf[base + 6] = c.floatValue;
|
||||
case "coatTintR": buf[base + 7] = c.floatValue;
|
||||
// matp2: vec4(coatTintG, coatTintB, transmission, transRough)
|
||||
case "coatTintG": buf[base + 8] = c.floatValue;
|
||||
case "coatTintB": buf[base + 9] = c.floatValue;
|
||||
case "transmission": buf[base + 10] = c.floatValue;
|
||||
case "transmissionRough": buf[base + 11] = c.floatValue;
|
||||
// matp3: vec4(ior, thinWall, subsurface, subsurfaceAnisotropy)
|
||||
case "ior": buf[base + 12] = c.floatValue;
|
||||
case "thinWall": buf[base + 13] = c.floatValue;
|
||||
case "subsurface": buf[base + 14] = c.floatValue;
|
||||
case "subsurfaceAnisotropy": buf[base + 15] = c.floatValue;
|
||||
// matp4: vec4(subsurfaceRadiusR, subsurfaceRadiusG, subsurfaceRadiusB, subsurfaceColorR)
|
||||
case "subsurfaceRadiusR": buf[base + 16] = c.floatValue;
|
||||
case "subsurfaceRadiusG": buf[base + 17] = c.floatValue;
|
||||
case "subsurfaceRadiusB": buf[base + 18] = c.floatValue;
|
||||
case "subsurfaceColorR": buf[base + 19] = c.floatValue;
|
||||
// matp5: vec4(subsurfaceColorG, subsurfaceColorB, sheenTintR, sheenTintG)
|
||||
case "subsurfaceColorG": buf[base + 20] = c.floatValue;
|
||||
case "subsurfaceColorB": buf[base + 21] = c.floatValue;
|
||||
case "sheenTintR": buf[base + 22] = c.floatValue;
|
||||
case "sheenTintG": buf[base + 23] = c.floatValue;
|
||||
// matp6: vec4(sheenTintB, specularTintR, specularTintG, specularTintB)
|
||||
case "sheenTintB": buf[base + 24] = c.floatValue;
|
||||
case "specularTintR": buf[base + 25] = c.floatValue;
|
||||
case "specularTintG": buf[base + 26] = c.floatValue;
|
||||
case "specularTintB": buf[base + 27] = c.floatValue;
|
||||
// matp7: vec4(subsurfaceScale, 0, 0, 0)
|
||||
case "subsurfaceScale": buf[base + 28] = c.floatValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
static var framePassed = true;
|
||||
public static function setActive(sceneName: String, done: Object->Void = null) {
|
||||
if (!framePassed) return;
|
||||
@ -249,6 +353,7 @@ class Scene {
|
||||
if (terrainStream != null) terrainStream.update(active.camera);
|
||||
#end
|
||||
for (anim in animations) anim.update(Time.delta);
|
||||
for (tilesheet in tilesheets) tilesheet.update();
|
||||
for (e in empties) if (e != null && e.parent != null) e.transform.update();
|
||||
}
|
||||
|
||||
@ -348,9 +453,23 @@ class Scene {
|
||||
return g;
|
||||
}
|
||||
|
||||
public function removeFromGroups(object: Object) {
|
||||
if (groups == null) return;
|
||||
for (name in groups.keys()) getGroup(name).remove(object);
|
||||
}
|
||||
|
||||
public function addMeshObject(data: MeshData, materials: Vector<MaterialData>, parent: Object = null): MeshObject {
|
||||
var object = new MeshObject(data, materials);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
#if (rp_renderer == "Deferred")
|
||||
markMaterialParamsDirty();
|
||||
#end
|
||||
return object;
|
||||
}
|
||||
|
||||
public function addCurveObject(data: TCurveData, parent: Object = null): CurveObject {
|
||||
var object = new CurveObject(data);
|
||||
parent != null ? object.setParent(parent) : object.setParent(root);
|
||||
return object;
|
||||
}
|
||||
|
||||
@ -613,6 +732,10 @@ class Scene {
|
||||
else done(ro);
|
||||
});
|
||||
}
|
||||
else if (o.type == "curve_object") {
|
||||
var object = addCurveObject(Data.getCurveRawByName(format.curve_datas, o.data_ref), parent);
|
||||
returnObject(object, o, done);
|
||||
}
|
||||
else done(null);
|
||||
}
|
||||
|
||||
@ -793,11 +916,13 @@ class Scene {
|
||||
}
|
||||
else { #end // lnx_skin
|
||||
#if lnx_stream
|
||||
streamMeshObject(
|
||||
if ((o.particle_refs == null || o.particle_refs.length == 0) && o.is_particle == null && parent != null)
|
||||
streamMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
else
|
||||
returnMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#else
|
||||
returnMeshObject(
|
||||
returnMeshObject(object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#end
|
||||
object_file, data_ref, sceneName, null, materials, parent, parentObject, o, done);
|
||||
#if lnx_skin
|
||||
}
|
||||
#end
|
||||
@ -872,20 +997,24 @@ class Scene {
|
||||
#end
|
||||
if (o.properties != null) {
|
||||
object.properties = new Map();
|
||||
for (p in o.properties) object.properties.set(p.name, p.value);
|
||||
for (p in o.properties) {
|
||||
object.properties.set(p.name, cleanValue(p.value));
|
||||
}
|
||||
}
|
||||
|
||||
if (o.vertex_groups != null) {
|
||||
object.vertex_groups = new Map();
|
||||
for (p in o.vertex_groups){
|
||||
var verts = [];
|
||||
for(i in 0...Std.int(p.value.length/3)){
|
||||
var x = Std.parseFloat(p.value[i*3]);
|
||||
var y = Std.parseFloat(p.value[i*3+1]);
|
||||
var z = Std.parseFloat(p.value[i*3+2]);
|
||||
verts.push(new iron.math.Vec4(x, y, z, 1));
|
||||
cast(object, MeshObject).vertexGroups = new Map();
|
||||
for (p in o.vertex_groups) {
|
||||
var verts:Array<iron.math.Vec4> = [];
|
||||
|
||||
var data:kha.arrays.Float32Array = cast p.value;
|
||||
|
||||
if (data != null) {
|
||||
for (i in 0...Std.int(data.length / 3)) {
|
||||
verts.push(new iron.math.Vec4(data[i * 3], data[i * 3 + 1], data[i * 3 + 2], 1.0));
|
||||
}
|
||||
}
|
||||
object.vertex_groups.set(p.name, verts);
|
||||
cast(object, MeshObject).vertexGroups.set(p.name, verts);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1049,4 +1178,16 @@ class Scene {
|
||||
public function notifyOnRemove(f: Void->Void) {
|
||||
traitRemoves.push(f);
|
||||
}
|
||||
|
||||
static function cleanValue(val: Dynamic): Dynamic {
|
||||
if (val == null) return null;
|
||||
if (untyped val.buffer != null) {
|
||||
var data: kha.arrays.Float32Array = cast val;
|
||||
return [for (i in 0...data.length) data[i]];
|
||||
}
|
||||
if (Std.isOfType(val, Array)) {
|
||||
return [for (item in (cast val: Array<Dynamic>)) cleanValue(item)];
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,10 +125,10 @@ class Trait {
|
||||
/**
|
||||
Add 2D render handler.
|
||||
**/
|
||||
public function notifyOnRender2D(f: kha.graphics2.Graphics->Void) {
|
||||
public function notifyOnRender2D(f: kha.graphics2.Graphics->Void, index: Int = -1) {
|
||||
if (_render2D == null) _render2D = [];
|
||||
_render2D.push(f);
|
||||
App.notifyOnRender2D(f);
|
||||
App.notifyOnRender2D(f, index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
20
leenkx/Sources/iron/culling/BVHNode.hx
Normal file
@ -0,0 +1,20 @@
|
||||
package iron.culling;
|
||||
|
||||
class BVHNode {
|
||||
public var minX: Float = 0;
|
||||
public var minY: Float = 0;
|
||||
public var minZ: Float = 0;
|
||||
public var maxX: Float = 0;
|
||||
public var maxY: Float = 0;
|
||||
public var maxZ: Float = 0;
|
||||
public var centerX: Float = 0;
|
||||
public var centerY: Float = 0;
|
||||
public var centerZ: Float = 0;
|
||||
public var isLeaf: Bool = false;
|
||||
public var leftChild: Int = -1;
|
||||
public var rightChild: Int = -1;
|
||||
public var firstObject: Int = 0;
|
||||
public var objectCount: Int = 0;
|
||||
|
||||
public function new() {}
|
||||
}
|
||||
62
leenkx/Sources/iron/culling/ContributionCull.hx
Normal file
@ -0,0 +1,62 @@
|
||||
package iron.culling;
|
||||
|
||||
class ContributionCull {
|
||||
|
||||
public static var minPixelRadius: Float = 2.0;
|
||||
public static var enabled: Bool = false;
|
||||
|
||||
public static function passes(
|
||||
centerX: Float, centerY: Float, centerZ: Float,
|
||||
radius: Float,
|
||||
camX: Float, camY: Float, camZ: Float,
|
||||
focalLengthPx: Float
|
||||
): Bool {
|
||||
if (!enabled || minPixelRadius <= 0.0 || focalLengthPx <= 0.0) return true;
|
||||
|
||||
var dx = centerX - camX;
|
||||
var dy = centerY - camY;
|
||||
var dz = centerZ - camZ;
|
||||
var dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (dist <= radius) return true;
|
||||
|
||||
var projectedRadiusPx = focalLengthPx * radius / dist;
|
||||
return projectedRadiusPx >= minPixelRadius;
|
||||
}
|
||||
|
||||
public static function passesNode(
|
||||
node: BVHNode,
|
||||
camX: Float, camY: Float, camZ: Float,
|
||||
focalLengthPx: Float
|
||||
): Bool {
|
||||
if (!enabled || minPixelRadius <= 0.0 || focalLengthPx <= 0.0) return true;
|
||||
|
||||
var cx = (node.minX + node.maxX) * 0.5;
|
||||
var cy = (node.minY + node.maxY) * 0.5;
|
||||
var cz = (node.minZ + node.maxZ) * 0.5;
|
||||
var ex = (node.maxX - node.minX) * 0.5;
|
||||
var ey = (node.maxY - node.minY) * 0.5;
|
||||
var ez = (node.maxZ - node.minZ) * 0.5;
|
||||
var radius = Math.sqrt(ex * ex + ey * ey + ez * ez);
|
||||
|
||||
if (camX >= node.minX && camX <= node.maxX &&
|
||||
camY >= node.minY && camY <= node.maxY &&
|
||||
camZ >= node.minZ && camZ <= node.maxZ) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var dx = cx - camX;
|
||||
var dy = cy - camY;
|
||||
var dz = cz - camZ;
|
||||
var dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||
|
||||
if (dist <= radius) return true;
|
||||
|
||||
var projectedRadiusPx = focalLengthPx * radius / dist;
|
||||
return projectedRadiusPx >= minPixelRadius;
|
||||
}
|
||||
|
||||
public static function computeFocalLengthPx(fov: Float, screenHeight: Int): Float {
|
||||
return screenHeight / (2.0 * Math.tan(fov * 0.5));
|
||||
}
|
||||
}
|
||||
373
leenkx/Sources/iron/culling/CullData.hx
Normal file
@ -0,0 +1,373 @@
|
||||
package iron.culling;
|
||||
|
||||
import iron.object.MeshObject;
|
||||
import iron.Scene;
|
||||
import kha.arrays.Float32Array;
|
||||
|
||||
class CullData {
|
||||
|
||||
static inline var STRIDE = 16;
|
||||
static inline var BASIS_STRIDE = 12;
|
||||
|
||||
public var data: Float32Array;
|
||||
public var prevData: Float32Array;
|
||||
public var uidData: kha.arrays.Int32Array;
|
||||
public var prevUidData: kha.arrays.Int32Array;
|
||||
var basisData: Float32Array;
|
||||
var prevBasisData: Float32Array;
|
||||
public var count: Int = 0;
|
||||
public var prevCount: Int = 0;
|
||||
public var anyMoved: Bool = false;
|
||||
public var anyFlagsChanged: Bool = false;
|
||||
public var orderChanged: Bool = false;
|
||||
|
||||
public var meshes: Array<MeshObject> = [];
|
||||
public var slotsDirty: Bool = true;
|
||||
public var slotsRebuilt: Bool = false;
|
||||
|
||||
public var visibleList: Array<Int>;
|
||||
public var visibleCount: Int = 0;
|
||||
|
||||
var staticFlagsCache: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
|
||||
|
||||
var matTranspCache: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
|
||||
var matSnapshots: haxe.ds.IntMap<haxe.ds.Vector<iron.data.MaterialData>> = new haxe.ds.IntMap();
|
||||
|
||||
var prevUidMap: haxe.ds.IntMap<Int> = new haxe.ds.IntMap<Int>();
|
||||
|
||||
public function new() {
|
||||
visibleList = [];
|
||||
}
|
||||
|
||||
public inline function stride(): Int {
|
||||
return STRIDE;
|
||||
}
|
||||
|
||||
public function ensureCapacity(cap: Int) {
|
||||
if (data == null || data.length < cap * STRIDE) {
|
||||
var newCap = cap;
|
||||
if (data != null) newCap = Std.int(Math.max(cap, data.length / STRIDE * 2));
|
||||
var oldData = data;
|
||||
var oldPrev = prevData;
|
||||
var oldUid = uidData;
|
||||
var oldPrevUid = prevUidData;
|
||||
var oldBasis = basisData;
|
||||
var oldPrevBasis = prevBasisData;
|
||||
data = new Float32Array(newCap * STRIDE);
|
||||
prevData = new Float32Array(newCap * STRIDE);
|
||||
uidData = new kha.arrays.Int32Array(newCap);
|
||||
prevUidData = new kha.arrays.Int32Array(newCap);
|
||||
basisData = new Float32Array(newCap * BASIS_STRIDE);
|
||||
prevBasisData = new Float32Array(newCap * BASIS_STRIDE);
|
||||
if (oldData != null) {
|
||||
var copyLen = oldData.length;
|
||||
for (i in 0...copyLen) data[i] = oldData[i];
|
||||
}
|
||||
if (oldPrev != null) {
|
||||
var copyLen = oldPrev.length;
|
||||
for (i in 0...copyLen) prevData[i] = oldPrev[i];
|
||||
}
|
||||
if (oldUid != null) {
|
||||
var copyLen = oldUid.length;
|
||||
for (i in 0...copyLen) uidData[i] = oldUid[i];
|
||||
}
|
||||
if (oldPrevUid != null) {
|
||||
var copyLen = oldPrevUid.length;
|
||||
for (i in 0...copyLen) prevUidData[i] = oldPrevUid[i];
|
||||
}
|
||||
if (oldBasis != null) {
|
||||
var copyLen = oldBasis.length;
|
||||
for (i in 0...copyLen) basisData[i] = oldBasis[i];
|
||||
}
|
||||
if (oldPrevBasis != null) {
|
||||
var copyLen = oldPrevBasis.length;
|
||||
for (i in 0...copyLen) prevBasisData[i] = oldPrevBasis[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function syncFromScene() {
|
||||
var sceneMeshes = Scene.active.meshes;
|
||||
slotsRebuilt = false;
|
||||
if (slotsDirty || meshes.length != sceneMeshes.length) {
|
||||
meshes = sceneMeshes.copy();
|
||||
slotsDirty = false;
|
||||
slotsRebuilt = true;
|
||||
staticFlagsCache.clear();
|
||||
matTranspCache.clear();
|
||||
matSnapshots.clear();
|
||||
}
|
||||
count = meshes.length;
|
||||
ensureCapacity(count);
|
||||
|
||||
var tmpD = prevData; prevData = data; data = tmpD;
|
||||
var tmpU = prevUidData; prevUidData = uidData; uidData = tmpU;
|
||||
var tmpB = prevBasisData; prevBasisData = basisData; basisData = tmpB;
|
||||
|
||||
prevUidMap.clear();
|
||||
var prevUidMapBuilt = false;
|
||||
anyMoved = false;
|
||||
anyFlagsChanged = false;
|
||||
orderChanged = false;
|
||||
|
||||
for (i in 0...count) {
|
||||
var mesh = meshes[i];
|
||||
var t = mesh.transform;
|
||||
var uid = mesh.uid;
|
||||
var p = mesh.parent;
|
||||
while (p != null) {
|
||||
p.transform.update();
|
||||
p = p.parent;
|
||||
}
|
||||
t.update();
|
||||
|
||||
var off = i * STRIDE;
|
||||
var r = t.radius;
|
||||
var wx = t.worldx();
|
||||
var wy = t.worldy();
|
||||
var wz = t.worldz();
|
||||
|
||||
var radiusScale = mesh.data.isSkinned ? 2.0 : 1.0;
|
||||
#if lnx_gpu_particles
|
||||
if (mesh.particleSystems != null || mesh.particleOwner != null) radiusScale *= 1000;
|
||||
#end
|
||||
if (mesh.data.geom.instanced) radiusScale *= 100;
|
||||
r *= radiusScale;
|
||||
|
||||
data[off + 3] = r;
|
||||
data[off + 4] = wx;
|
||||
data[off + 5] = wy;
|
||||
data[off + 6] = wz;
|
||||
|
||||
var w = t.world;
|
||||
|
||||
var prevIdx: Null<Int> = null;
|
||||
if (prevUidData != null && i < prevCount && prevUidData[i] == uid) {
|
||||
prevIdx = i;
|
||||
} else {
|
||||
if (!prevUidMapBuilt) {
|
||||
prevUidMapBuilt = true;
|
||||
if (prevUidData != null) {
|
||||
for (pi in 0...prevCount) prevUidMap.set(prevUidData[pi], pi);
|
||||
}
|
||||
}
|
||||
prevIdx = prevUidMap.get(uid);
|
||||
}
|
||||
|
||||
if (prevIdx != null && prevIdx != i) orderChanged = true;
|
||||
var aabbCached = false;
|
||||
if (prevIdx != null) {
|
||||
var poff = prevIdx * STRIDE;
|
||||
var pb = prevIdx * BASIS_STRIDE;
|
||||
if (prevData[poff + 4] == wx && prevData[poff + 5] == wy && prevData[poff + 6] == wz &&
|
||||
prevBasisData[pb + 0] == w._00 && prevBasisData[pb + 1] == w._10 && prevBasisData[pb + 2] == w._20 &&
|
||||
prevBasisData[pb + 3] == w._01 && prevBasisData[pb + 4] == w._11 && prevBasisData[pb + 5] == w._21 &&
|
||||
prevBasisData[pb + 6] == w._02 && prevBasisData[pb + 7] == w._12 && prevBasisData[pb + 8] == w._22 &&
|
||||
prevBasisData[pb + 9] == t.dim.x && prevBasisData[pb + 10] == t.dim.y && prevBasisData[pb + 11] == t.dim.z) {
|
||||
data[off + 0] = prevData[poff + 0];
|
||||
data[off + 1] = prevData[poff + 1];
|
||||
data[off + 2] = prevData[poff + 2];
|
||||
data[off + 8] = prevData[poff + 8];
|
||||
data[off + 9] = prevData[poff + 9];
|
||||
data[off + 10] = prevData[poff + 10];
|
||||
aabbCached = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!aabbCached) {
|
||||
var r00 = w._00, r10 = w._10, r20 = w._20;
|
||||
var r01 = w._01, r11 = w._11, r21 = w._21;
|
||||
var r02 = w._02, r12 = w._12, r22 = w._22;
|
||||
var lenX = Math.sqrt(r00 * r00 + r01 * r01 + r02 * r02);
|
||||
var lenY = Math.sqrt(r10 * r10 + r11 * r11 + r12 * r12);
|
||||
var lenZ = Math.sqrt(r20 * r20 + r21 * r21 + r22 * r22);
|
||||
if (lenX > 1e-8) { r00 /= lenX; r01 /= lenX; r02 /= lenX; }
|
||||
if (lenY > 1e-8) { r10 /= lenY; r11 /= lenY; r12 /= lenY; }
|
||||
if (lenZ > 1e-8) { r20 /= lenZ; r21 /= lenZ; r22 /= lenZ; }
|
||||
|
||||
var hx = t.dim.x * 0.5 * radiusScale;
|
||||
var hy = t.dim.y * 0.5 * radiusScale;
|
||||
var hz = t.dim.z * 0.5 * radiusScale;
|
||||
|
||||
var aabbMinX = 1e20, aabbMinY = 1e20, aabbMinZ = 1e20;
|
||||
var aabbMaxX = -1e20, aabbMaxY = -1e20, aabbMaxZ = -1e20;
|
||||
for (c in 0...8) {
|
||||
var cx = (c & 1) != 0 ? hx : -hx;
|
||||
var cy = (c & 2) != 0 ? hy : -hy;
|
||||
var cz = (c & 4) != 0 ? hz : -hz;
|
||||
var px = cx * r00 + cy * r10 + cz * r20 + wx;
|
||||
var py = cx * r01 + cy * r11 + cz * r21 + wy;
|
||||
var pz = cx * r02 + cy * r12 + cz * r22 + wz;
|
||||
if (px < aabbMinX) aabbMinX = px;
|
||||
if (py < aabbMinY) aabbMinY = py;
|
||||
if (pz < aabbMinZ) aabbMinZ = pz;
|
||||
if (px > aabbMaxX) aabbMaxX = px;
|
||||
if (py > aabbMaxY) aabbMaxY = py;
|
||||
if (pz > aabbMaxZ) aabbMaxZ = pz;
|
||||
}
|
||||
|
||||
data[off + 0] = aabbMinX;
|
||||
data[off + 1] = aabbMinY;
|
||||
data[off + 2] = aabbMinZ;
|
||||
|
||||
data[off + 8] = aabbMaxX;
|
||||
data[off + 9] = aabbMaxY;
|
||||
data[off + 10] = aabbMaxZ;
|
||||
}
|
||||
|
||||
var flags: Int;
|
||||
var cachedFlags = staticFlagsCache.get(uid);
|
||||
if (cachedFlags != null) {
|
||||
flags = cachedFlags;
|
||||
} else {
|
||||
flags = 0;
|
||||
if (mesh.raw != null && mesh.raw.properties != null) {
|
||||
for (prop in mesh.raw.properties) {
|
||||
if (prop.name == "occluder" && prop.value == true) {
|
||||
flags |= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((flags & 1) == 0 && mesh.data != null &&
|
||||
mesh.data.raw != null && mesh.data.raw.occluder_proxy != null) {
|
||||
flags |= 1;
|
||||
}
|
||||
if (mesh.raw != null && mesh.raw.visible_shadow != false) flags |= 4;
|
||||
staticFlagsCache.set(uid, flags);
|
||||
}
|
||||
var mats = mesh.materials;
|
||||
var snap = matSnapshots.get(uid);
|
||||
var matsChanged = snap == null || mats == null || snap.length != mats.length;
|
||||
if (!matsChanged) {
|
||||
for (mi in 0...mats.length) {
|
||||
if (snap[mi] != mats[mi]) { matsChanged = true; break; }
|
||||
}
|
||||
}
|
||||
if (matsChanged) {
|
||||
var tbit = 0;
|
||||
if (mats != null) {
|
||||
for (mi in 0...mats.length) {
|
||||
var mat = mats[mi];
|
||||
if (mat == null || mat.shader == null) continue;
|
||||
for (sc in mat.shader.contexts) {
|
||||
var sr = sc.raw;
|
||||
if (sr != null && sr.name != null && sr.name.substr(0, 4) == "mesh" &&
|
||||
(sr.depth_write != true || sr.blend_source != null)) {
|
||||
tbit = 32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tbit != 0) break;
|
||||
}
|
||||
}
|
||||
matTranspCache.set(uid, tbit);
|
||||
var nsnap = new haxe.ds.Vector<iron.data.MaterialData>(mats != null ? mats.length : 0);
|
||||
for (mi in 0...nsnap.length) nsnap[mi] = mats[mi];
|
||||
matSnapshots.set(uid, nsnap);
|
||||
}
|
||||
var tbitCached = matTranspCache.get(uid);
|
||||
if (tbitCached != null) flags |= tbitCached;
|
||||
if (mesh.visible) flags |= 8;
|
||||
if (radiusScale != 1.0) flags |= 16;
|
||||
if (prevIdx != null) {
|
||||
var poff = prevIdx * STRIDE;
|
||||
if (prevData[poff + 0] != data[off + 0] ||
|
||||
prevData[poff + 1] != data[off + 1] ||
|
||||
prevData[poff + 2] != data[off + 2] ||
|
||||
prevData[poff + 8] != data[off + 8] ||
|
||||
prevData[poff + 9] != data[off + 9] ||
|
||||
prevData[poff + 10] != data[off + 10]) {
|
||||
flags |= 2;
|
||||
anyMoved = true;
|
||||
}
|
||||
} else {
|
||||
flags |= 2;
|
||||
anyMoved = true;
|
||||
anyFlagsChanged = true;
|
||||
}
|
||||
if (prevIdx != null) {
|
||||
var poff = prevIdx * STRIDE;
|
||||
if (((flags ^ Std.int(prevData[poff + 7])) & ~2) != 0) anyFlagsChanged = true;
|
||||
}
|
||||
data[off + 7] = flags;
|
||||
|
||||
var boff = i * BASIS_STRIDE;
|
||||
basisData[boff + 0] = w._00;
|
||||
basisData[boff + 1] = w._10;
|
||||
basisData[boff + 2] = w._20;
|
||||
basisData[boff + 3] = w._01;
|
||||
basisData[boff + 4] = w._11;
|
||||
basisData[boff + 5] = w._21;
|
||||
basisData[boff + 6] = w._02;
|
||||
basisData[boff + 7] = w._12;
|
||||
basisData[boff + 8] = w._22;
|
||||
basisData[boff + 9] = t.dim.x;
|
||||
basisData[boff + 10] = t.dim.y;
|
||||
basisData[boff + 11] = t.dim.z;
|
||||
uidData[i] = uid;
|
||||
}
|
||||
|
||||
prevCount = count;
|
||||
}
|
||||
|
||||
public inline function getMinX(i: Int): Float {
|
||||
return data[i * STRIDE + 0];
|
||||
}
|
||||
public inline function getMinY(i: Int): Float {
|
||||
return data[i * STRIDE + 1];
|
||||
}
|
||||
public inline function getMinZ(i: Int): Float {
|
||||
return data[i * STRIDE + 2];
|
||||
}
|
||||
public inline function getRadius(i: Int): Float {
|
||||
return data[i * STRIDE + 3];
|
||||
}
|
||||
public inline function getCenterX(i: Int): Float {
|
||||
return data[i * STRIDE + 4];
|
||||
}
|
||||
public inline function getCenterY(i: Int): Float {
|
||||
return data[i * STRIDE + 5];
|
||||
}
|
||||
public inline function getCenterZ(i: Int): Float {
|
||||
return data[i * STRIDE + 6];
|
||||
}
|
||||
public inline function getFlags(i: Int): Int {
|
||||
return Std.int(data[i * STRIDE + 7]);
|
||||
}
|
||||
public inline function isOccluder(i: Int): Bool {
|
||||
return (getFlags(i) & 1) != 0;
|
||||
}
|
||||
public inline function isDynamic(i: Int): Bool {
|
||||
return (getFlags(i) & 2) != 0;
|
||||
}
|
||||
public inline function isShadowCaster(i: Int): Bool {
|
||||
return (getFlags(i) & 4) != 0;
|
||||
}
|
||||
public inline function isMeshVisible(i: Int): Bool {
|
||||
return (getFlags(i) & 8) != 0;
|
||||
}
|
||||
public inline function getObjectID(i: Int): Int {
|
||||
return uidData[i];
|
||||
}
|
||||
|
||||
public inline function getMaxX(i: Int): Float {
|
||||
return data[i * STRIDE + 8];
|
||||
}
|
||||
public inline function getMaxY(i: Int): Float {
|
||||
return data[i * STRIDE + 9];
|
||||
}
|
||||
public inline function getMaxZ(i: Int): Float {
|
||||
return data[i * STRIDE + 10];
|
||||
}
|
||||
|
||||
public function clearVisible() {
|
||||
visibleCount = 0;
|
||||
if (visibleList.length > 0) visibleList.resize(0);
|
||||
}
|
||||
|
||||
public function addVisible(objectIndex: Int) {
|
||||
visibleList.push(objectIndex);
|
||||
visibleCount++;
|
||||
}
|
||||
}
|
||||
773
leenkx/Sources/iron/culling/CullSystem.hx
Normal file
@ -0,0 +1,773 @@
|
||||
package iron.culling;
|
||||
|
||||
import iron.object.CameraObject;
|
||||
import iron.object.FrustumPlane;
|
||||
import iron.Scene;
|
||||
import iron.RenderPath;
|
||||
import iron.math.Mat4;
|
||||
|
||||
class CullSystem {
|
||||
|
||||
public static var active: CullSystem = null;
|
||||
|
||||
public var tier: CullTier;
|
||||
public var cullData: CullData;
|
||||
public var rasterizer: SoftwareOcclusionRasterizer;
|
||||
public var shadowRasterizer: SoftwareOcclusionRasterizer;
|
||||
public var occluderProxies: Array<OccluderProxy>;
|
||||
public var bvh: ObjectBVH;
|
||||
public var temporal: TemporalState;
|
||||
|
||||
public var enabled: Bool = true;
|
||||
public var occlusionEnabled: Bool = true;
|
||||
public var bvhDirty: Bool = true;
|
||||
public var proxiesDirty: Bool = true;
|
||||
var dataSyncedFrame: Int = -1;
|
||||
|
||||
var focalLengthPx: Float = 500.0;
|
||||
var orthoPxPerUnit: Float = 0.0;
|
||||
var screenW: Int = 1920;
|
||||
var screenH: Int = 1080;
|
||||
|
||||
public static var maxOccluders: Int = 48;
|
||||
public static var maxOccluderTris: Int = 4000;
|
||||
public static var maxTrisPerProxy: Int = 768;
|
||||
public static var maxShadowOccluders: Int = 24;
|
||||
public static var maxShadowOccluderTris: Int = 1536;
|
||||
public static var boxOccluders: Bool = false;
|
||||
public static var shadowBoxOccluders: Bool = false;
|
||||
public static var shadowBoxShrink: Float = 0.7;
|
||||
public static var shadowCullingEnabled: Bool = true;
|
||||
public static var minOccluderPx: Float = 24.0;
|
||||
public static var boxShrink: Float = 0.5;
|
||||
public static var depthEps: Float = 1e-6;
|
||||
|
||||
public static var shadowCullInterval: Int = 2;
|
||||
|
||||
static inline var MAX_SHADOW_SLOTS = 8;
|
||||
var shadowPasses: haxe.ds.ObjectMap<iron.object.LightObject, Array<ShadowPassSlot>> = new haxe.ds.ObjectMap();
|
||||
var shadowCallIdx: Map<iron.object.LightObject, Int> = new Map();
|
||||
var shadowCallFrame: Map<iron.object.LightObject, Int> = new Map();
|
||||
var lastShadowSlot: ShadowPassSlot = null;
|
||||
var lastShadowFrame: Int = -1;
|
||||
var lastShadowCallIdx: Int = -1;
|
||||
#if lnx_csm
|
||||
var shadowVps: haxe.ds.ObjectMap<iron.object.LightObject, Mat4> = new haxe.ds.ObjectMap();
|
||||
#end
|
||||
var sceneRev: Int = 0;
|
||||
var sceneRevFrame: Int = -1;
|
||||
var frameCounter: Int = 0;
|
||||
var bvhSyncedFrame: Int = -1; // Frame when BVH was built/refit
|
||||
|
||||
var occlusionFrustumPlanes: Array<FrustumPlane> = null;
|
||||
var frustumPlanes: Array<FrustumPlane> = null;
|
||||
|
||||
var shadowFrustumPlanes: Array<FrustumPlane> = null;
|
||||
|
||||
var uidToProxy: haxe.ds.IntMap<OccluderProxy> = new haxe.ds.IntMap<OccluderProxy>();
|
||||
var uidScan: haxe.ds.IntMap<Bool> = new haxe.ds.IntMap<Bool>();
|
||||
|
||||
var prevVP: Mat4 = null;
|
||||
#if lnx_taa
|
||||
var unjitteredVP: Mat4 = null;
|
||||
#end
|
||||
public var shadowFlagsValid: Bool = false;
|
||||
|
||||
var inFrustumList: Array<Int> = [];
|
||||
var selIdx: Array<Int> = [];
|
||||
var selScore: Array<Float> = [];
|
||||
var selCount: Int = 0;
|
||||
|
||||
public function new() {
|
||||
cullData = new CullData();
|
||||
rasterizer = new SoftwareOcclusionRasterizer(128, 64);
|
||||
shadowRasterizer = new SoftwareOcclusionRasterizer(128, 128);
|
||||
occluderProxies = [];
|
||||
temporal = new TemporalState();
|
||||
tier = detectTier();
|
||||
}
|
||||
|
||||
static inline var MAT_EPS = 1e-7;
|
||||
|
||||
static inline function matEquals(a: Mat4, b: Mat4): Bool {
|
||||
return
|
||||
Math.abs(a._00 - b._00) < MAT_EPS && Math.abs(a._10 - b._10) < MAT_EPS &&
|
||||
Math.abs(a._20 - b._20) < MAT_EPS && Math.abs(a._30 - b._30) < MAT_EPS &&
|
||||
Math.abs(a._01 - b._01) < MAT_EPS && Math.abs(a._11 - b._11) < MAT_EPS &&
|
||||
Math.abs(a._21 - b._21) < MAT_EPS && Math.abs(a._31 - b._31) < MAT_EPS &&
|
||||
Math.abs(a._02 - b._02) < MAT_EPS && Math.abs(a._12 - b._12) < MAT_EPS &&
|
||||
Math.abs(a._22 - b._22) < MAT_EPS && Math.abs(a._32 - b._32) < MAT_EPS &&
|
||||
Math.abs(a._03 - b._03) < MAT_EPS && Math.abs(a._13 - b._13) < MAT_EPS &&
|
||||
Math.abs(a._23 - b._23) < MAT_EPS && Math.abs(a._33 - b._33) < MAT_EPS;
|
||||
}
|
||||
|
||||
public static function detectTier(): CullTier {
|
||||
#if (target.threaded)
|
||||
return CPUMultiThread;
|
||||
#else
|
||||
return CPUSingleThread;
|
||||
#end
|
||||
}
|
||||
|
||||
public function cull(camera: CameraObject) {
|
||||
frameCounter = RenderPath.active != null ? RenderPath.active.frame : frameCounter + 1;
|
||||
if (!enabled || Scene.active == null || Scene.active.meshes.length == 0) {
|
||||
if (Scene.active != null) {
|
||||
var meshes = Scene.active.meshes;
|
||||
for (i in 0...meshes.length) {
|
||||
meshes[i].occlusionVisible = true;
|
||||
meshes[i].shadowOcclusionVisible = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var rp = RenderPath.active;
|
||||
if (rp != null) {
|
||||
screenW = rp.currentW;
|
||||
screenH = rp.currentH;
|
||||
}
|
||||
|
||||
var targetW = Std.int(Math.max(128, Math.min(512, screenW >> 4)));
|
||||
var targetH = Std.int(Math.max(64, Math.min(256, screenH >> 4)));
|
||||
if (rasterizer.width < (targetW >> 1) || rasterizer.width > (targetW << 1) ||
|
||||
rasterizer.height < (targetH >> 1) || rasterizer.height > (targetH << 1)) {
|
||||
rasterizer = new SoftwareOcclusionRasterizer(targetW, targetH);
|
||||
}
|
||||
|
||||
if (camera.data.raw.ortho == null) {
|
||||
focalLengthPx = ContributionCull.computeFocalLengthPx(
|
||||
camera.data.raw.fov, screenH
|
||||
);
|
||||
orthoPxPerUnit = 0.0;
|
||||
} else {
|
||||
focalLengthPx = 0;
|
||||
var o = camera.data.raw.ortho;
|
||||
var halfH = (o[3] - o[2]) * 0.5;
|
||||
orthoPxPerUnit = halfH > 1e-9 ? screenH * 0.5 / halfH : 0.0;
|
||||
}
|
||||
rasterizer.nearClip = camera.data.raw.ortho == null ?
|
||||
camera.data.raw.near_plane : 0.0001;
|
||||
|
||||
if (dataSyncedFrame != frameCounter) {
|
||||
cullData.syncFromScene();
|
||||
dataSyncedFrame = frameCounter;
|
||||
}
|
||||
if (cullData.slotsRebuilt) temporal.clear();
|
||||
if (sceneRevFrame != frameCounter) {
|
||||
sceneRevFrame = frameCounter;
|
||||
if (cullData.anyMoved || cullData.anyFlagsChanged || cullData.orderChanged) sceneRev++;
|
||||
}
|
||||
var cd = cullData;
|
||||
var meshes = cd.meshes;
|
||||
|
||||
#if lnx_taa
|
||||
if (unjitteredVP == null) unjitteredVP = Mat4.identity();
|
||||
unjitteredVP.multmats(camera.noJitterP, camera.V);
|
||||
var cmpVP = unjitteredVP;
|
||||
#else
|
||||
var cmpVP = camera.VP;
|
||||
#end
|
||||
var vpSame = prevVP != null && matEquals(cmpVP, prevVP);
|
||||
if (vpSame && !cd.anyMoved && !cd.anyFlagsChanged && !cd.orderChanged && !bvhDirty) {
|
||||
return;
|
||||
}
|
||||
var needBvhRebuild = bvhDirty;
|
||||
bvhDirty = false;
|
||||
if (prevVP == null) prevVP = Mat4.identity();
|
||||
prevVP.setFrom(cmpVP);
|
||||
|
||||
if (occlusionFrustumPlanes == null) {
|
||||
occlusionFrustumPlanes = [];
|
||||
for (i in 0...6) occlusionFrustumPlanes.push(new FrustumPlane());
|
||||
}
|
||||
{
|
||||
var vp = camera.VP;
|
||||
var p = occlusionFrustumPlanes;
|
||||
p[0].setComponents(vp._03 + vp._00, vp._13 + vp._10, vp._23 + vp._20, vp._33 + vp._30);
|
||||
p[1].setComponents(vp._03 - vp._00, vp._13 - vp._10, vp._23 - vp._20, vp._33 - vp._30);
|
||||
p[2].setComponents(vp._03 - vp._01, vp._13 - vp._11, vp._23 - vp._21, vp._33 - vp._31);
|
||||
p[3].setComponents(vp._03 + vp._01, vp._13 + vp._11, vp._23 + vp._21, vp._33 + vp._31);
|
||||
p[4].setComponents(vp._02, vp._12, vp._22, vp._32);
|
||||
p[5].setComponents(vp._03 - vp._02, vp._13 - vp._12, vp._23 - vp._22, vp._33 - vp._32);
|
||||
}
|
||||
frustumPlanes = occlusionFrustumPlanes;
|
||||
|
||||
var camX = camera.transform.worldx();
|
||||
var camY = camera.transform.worldy();
|
||||
var camZ = camera.transform.worldz();
|
||||
|
||||
if (bvh == null) bvh = new ObjectBVH();
|
||||
if (bvhSyncedFrame != frameCounter) {
|
||||
if (needBvhRebuild || bvh.root < 0) {
|
||||
bvh.build(cd);
|
||||
} else if (cd.anyMoved || cd.orderChanged) {
|
||||
bvh.refit(cd);
|
||||
}
|
||||
bvhSyncedFrame = frameCounter;
|
||||
}
|
||||
inFrustumList.resize(0);
|
||||
var planes = frustumPlanes;
|
||||
var fpx = focalLengthPx;
|
||||
bvh.traverseFrontToBack(camX, camY, camZ,
|
||||
function(node: BVHNode): Bool {
|
||||
if (!ObjectBVH.aabbInFrustum(node, planes)) return true;
|
||||
if (!ContributionCull.passesNode(node, camX, camY, camZ, fpx)) return true;
|
||||
return false;
|
||||
},
|
||||
function(objIdx: Int, _: Int) {
|
||||
if ((cd.getFlags(objIdx) & 8) == 0) return;
|
||||
if (!ContributionCull.passes(
|
||||
cd.getCenterX(objIdx), cd.getCenterY(objIdx), cd.getCenterZ(objIdx),
|
||||
cd.getRadius(objIdx), camX, camY, camZ, fpx
|
||||
)) return;
|
||||
inFrustumList.push(objIdx);
|
||||
}
|
||||
);
|
||||
cullData.clearVisible();
|
||||
|
||||
if (occlusionEnabled) {
|
||||
selectOccluders(meshes, camX, camY, camZ);
|
||||
|
||||
for (i in 0...meshes.length) {
|
||||
meshes[i].occlusionVisible = true;
|
||||
}
|
||||
|
||||
rasterizer.clear();
|
||||
if (selCount > 0) rasterizeSelected(meshes, camera.VP);
|
||||
var trisDone = rasterizer.trianglesRasterized;
|
||||
if (trisDone > 0) rasterizer.buildHiZ();
|
||||
|
||||
if (trisDone > 0) {
|
||||
testVisibilityBVH(camera.VP, camX, camY, camZ, planes);
|
||||
} else {
|
||||
for (k in 0...inFrustumList.length) {
|
||||
var objIdx = inFrustumList[k];
|
||||
cd.addVisible(objIdx);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (i in 0...meshes.length) {
|
||||
meshes[i].occlusionVisible = true;
|
||||
}
|
||||
for (k in 0...inFrustumList.length) {
|
||||
var objIdx = inFrustumList[k];
|
||||
cd.addVisible(objIdx);
|
||||
}
|
||||
}
|
||||
|
||||
temporal.advanceFrame();
|
||||
}
|
||||
|
||||
public function cullShadow(light: iron.object.LightObject) {
|
||||
if (!occlusionEnabled || light == null || Scene.active == null) return;
|
||||
if (!shadowCullingEnabled) {
|
||||
var ms = Scene.active.meshes;
|
||||
for (m in ms) m.shadowOcclusionVisible = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var frame = RenderPath.active != null ? RenderPath.active.frame : frameCounter;
|
||||
var vp = light.VP;
|
||||
if (vp == null) return;
|
||||
|
||||
var cd = cullData;
|
||||
|
||||
if (dataSyncedFrame != frame) {
|
||||
cd.syncFromScene();
|
||||
dataSyncedFrame = frame;
|
||||
}
|
||||
if (sceneRevFrame != frame) {
|
||||
sceneRevFrame = frame;
|
||||
if (cd.anyMoved || cd.anyFlagsChanged || cd.orderChanged) sceneRev++;
|
||||
}
|
||||
var meshes = cd.meshes;
|
||||
|
||||
var slots = shadowPasses.get(light);
|
||||
if (slots == null) {
|
||||
slots = [];
|
||||
shadowPasses.set(light, slots);
|
||||
}
|
||||
var callIdx = 0;
|
||||
{
|
||||
var f = shadowCallFrame.get(light);
|
||||
callIdx = (f == frame) ? shadowCallIdx.get(light) + 1 : 0;
|
||||
shadowCallIdx.set(light, callIdx);
|
||||
shadowCallFrame.set(light, frame);
|
||||
}
|
||||
var slotIdx = callIdx < MAX_SHADOW_SLOTS ? callIdx : MAX_SHADOW_SLOTS - 1;
|
||||
while (slots.length <= slotIdx) slots.push(new ShadowPassSlot());
|
||||
var slot = slots[slotIdx];
|
||||
|
||||
if (slot != null && slot == lastShadowSlot && lastShadowFrame == frame &&
|
||||
lastShadowCallIdx == callIdx && slot.appliedRev == sceneRev) return;
|
||||
|
||||
for (i in 0...meshes.length) {
|
||||
meshes[i].shadowOcclusionVisible = true;
|
||||
}
|
||||
|
||||
#if lnx_csm
|
||||
var isCsmLight = light.data.raw.type == "sun" &&
|
||||
iron.object.LightObject.cascadeCount > 1;
|
||||
if (isCsmLight && callIdx == 0) {
|
||||
var vp0 = shadowVps.get(light);
|
||||
if (vp0 == null) { vp0 = Mat4.identity(); shadowVps.set(light, vp0); }
|
||||
vp0.setFrom(vp);
|
||||
}
|
||||
#end
|
||||
|
||||
if (shadowCullInterval > 1) {
|
||||
if ((frame + slotIdx) % shadowCullInterval != 0) {
|
||||
var applied = false;
|
||||
#if lnx_csm
|
||||
if (isCsmLight && callIdx > 0) {
|
||||
var head = slots[0];
|
||||
var vp0 = shadowVps.get(light);
|
||||
if (head.appliedRev == sceneRev && head.flagsLen > 0 &&
|
||||
head.vp != null && vp0 != null && matEquals(vp0, head.vp)) {
|
||||
var hn = head.flagsLen < meshes.length ? head.flagsLen : meshes.length;
|
||||
for (i in 0...hn) meshes[i].shadowOcclusionVisible = head.flags[i];
|
||||
}
|
||||
applied = true;
|
||||
}
|
||||
#end
|
||||
if (!applied && slot.appliedRev == sceneRev && slot.flagsLen > 0 &&
|
||||
slot.vp != null && matEquals(vp, slot.vp)) {
|
||||
var n = slot.flagsLen < meshes.length ? slot.flagsLen : meshes.length;
|
||||
for (i in 0...n) meshes[i].shadowOcclusionVisible = slot.flags[i];
|
||||
}
|
||||
lastShadowSlot = slot;
|
||||
lastShadowFrame = frame;
|
||||
lastShadowCallIdx = callIdx;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (bvh == null) bvh = new ObjectBVH();
|
||||
if (bvhSyncedFrame != frame) {
|
||||
if (bvhDirty || bvh.root < 0) {
|
||||
bvh.build(cd);
|
||||
} else if (cd.anyMoved || cd.orderChanged) {
|
||||
bvh.refit(cd);
|
||||
}
|
||||
bvhSyncedFrame = frame;
|
||||
}
|
||||
|
||||
if (shadowFrustumPlanes == null) {
|
||||
shadowFrustumPlanes = [];
|
||||
for (i in 0...6) shadowFrustumPlanes.push(new FrustumPlane());
|
||||
}
|
||||
{
|
||||
var p = shadowFrustumPlanes;
|
||||
p[0].setComponents(vp._03 + vp._00, vp._13 + vp._10, vp._23 + vp._20, vp._33 + vp._30);
|
||||
p[1].setComponents(vp._03 - vp._00, vp._13 - vp._10, vp._23 - vp._20, vp._33 - vp._30);
|
||||
p[2].setComponents(vp._03 - vp._01, vp._13 - vp._11, vp._23 - vp._21, vp._33 - vp._31);
|
||||
p[3].setComponents(vp._03 + vp._01, vp._13 + vp._11, vp._23 + vp._21, vp._33 + vp._31);
|
||||
p[4].setComponents(vp._02, vp._12, vp._22, vp._32);
|
||||
p[5].setComponents(vp._03 - vp._02, vp._13 - vp._12, vp._23 - vp._22, vp._33 - vp._32);
|
||||
}
|
||||
var lightPlanes = shadowFrustumPlanes;
|
||||
|
||||
var lx = light.transform.worldx();
|
||||
var ly = light.transform.worldy();
|
||||
var lz = light.transform.worldz();
|
||||
|
||||
var raster = slot.rasterizer;
|
||||
var vpSame = slot.vp != null && matEquals(vp, slot.vp);
|
||||
if (slot.sceneRev == sceneRev && vpSame && raster != null) {
|
||||
// skip
|
||||
} else {
|
||||
if (raster == null) {
|
||||
raster = new SoftwareOcclusionRasterizer(
|
||||
shadowRasterizer.width, shadowRasterizer.height);
|
||||
slot.rasterizer = raster;
|
||||
}
|
||||
if (slot.vp == null) slot.vp = Mat4.identity();
|
||||
slot.vp.setFrom(vp);
|
||||
|
||||
var perspective = light.data.raw.type != "sun";
|
||||
selCount = 0;
|
||||
bvh.traverseFrontToBack(lx, ly, lz,
|
||||
function(node: BVHNode): Bool {
|
||||
if (!ObjectBVH.aabbInFrustum(node, lightPlanes)) return true;
|
||||
return false;
|
||||
},
|
||||
function(i: Int, _: Int) {
|
||||
if ((cd.getFlags(i) & 8) == 0) return;
|
||||
if ((cd.getFlags(i) & 16) != 0) return;
|
||||
if ((cd.getFlags(i) & 4) == 0) return;
|
||||
if ((cd.getFlags(i) & 32) != 0) return;
|
||||
if (i >= meshes.length) return;
|
||||
var r = cd.getRadius(i);
|
||||
var score = r;
|
||||
if (perspective) {
|
||||
var dx = cd.getCenterX(i) - lx;
|
||||
var dy = cd.getCenterY(i) - ly;
|
||||
var dz = cd.getCenterZ(i) - lz;
|
||||
var dist2 = dx * dx + dy * dy + dz * dz;
|
||||
if (dist2 <= r * r) return;
|
||||
score = r / Math.sqrt(dist2);
|
||||
}
|
||||
insertTopK(i, score, maxShadowOccluders);
|
||||
}
|
||||
);
|
||||
|
||||
raster = slot.rasterizer;
|
||||
raster.clear();
|
||||
var triBudget = maxShadowOccluderTris;
|
||||
for (s in 0...selCount) {
|
||||
if (triBudget < 12) break;
|
||||
var i = selIdx[s];
|
||||
var proxy = uidToProxy.get(cd.getObjectID(i));
|
||||
if (proxy != null && proxy.triangleCount > 0 &&
|
||||
proxy.triangleCount <= maxTrisPerProxy && proxy.triangleCount <= triBudget) {
|
||||
proxy.rasterize(raster, meshes[i].transform.world, vp,
|
||||
raster.width, raster.height);
|
||||
triBudget -= proxy.triangleCount;
|
||||
} else if (shadowBoxOccluders) {
|
||||
rasterizeBoxOccluder(raster, i, vp, shadowBoxShrink);
|
||||
triBudget -= 12;
|
||||
}
|
||||
}
|
||||
if (raster.trianglesRasterized > 0) raster.buildHiZ();
|
||||
}
|
||||
|
||||
if (raster.trianglesRasterized > 0) {
|
||||
var sw = raster.width;
|
||||
var sh = raster.height;
|
||||
|
||||
bvh.traverseFrontToBack(lx, ly, lz,
|
||||
function(node: BVHNode): Bool {
|
||||
if (!ObjectBVH.aabbInFrustum(node, lightPlanes)) return true;
|
||||
var anyVisible = false;
|
||||
if (node.isLeaf) {
|
||||
for (j in 0...node.objectCount) {
|
||||
var idx = bvh.objectIndices[node.firstObject + j];
|
||||
if ((cd.getFlags(idx) & 8) != 0 && idx < meshes.length) {
|
||||
anyVisible = true; break;
|
||||
}
|
||||
}
|
||||
if (!anyVisible) return true;
|
||||
}
|
||||
if (raster.isOccluded(
|
||||
node.minX, node.minY, node.minZ,
|
||||
node.maxX, node.maxY, node.maxZ,
|
||||
vp, sw, sh
|
||||
)) {
|
||||
bvh.forEachLeafInSubtree(node, function(idx: Int) {
|
||||
if (idx < meshes.length && (cd.getFlags(idx) & 8) != 0) {
|
||||
meshes[idx].shadowOcclusionVisible = false;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
function(objIdx: Int, _: Int) {
|
||||
if ((cd.getFlags(objIdx) & 8) == 0) return;
|
||||
if (objIdx >= meshes.length) return;
|
||||
meshes[objIdx].shadowOcclusionVisible = true;
|
||||
if (raster.isOccluded(
|
||||
cd.getMinX(objIdx), cd.getMinY(objIdx), cd.getMinZ(objIdx),
|
||||
cd.getMaxX(objIdx), cd.getMaxY(objIdx), cd.getMaxZ(objIdx),
|
||||
vp, sw, sh
|
||||
)) {
|
||||
meshes[objIdx].shadowOcclusionVisible = false;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#if lnx_csm
|
||||
if (isCsmLight) {
|
||||
for (c in 1...iron.object.LightObject.cascadeCount) {
|
||||
if (c >= slots.length) break;
|
||||
var s = slots[c];
|
||||
if (s == null || s == slot || s.vp == null || s.rasterizer == null) continue;
|
||||
for (i in 0...meshes.length) {
|
||||
if (meshes[i].shadowOcclusionVisible) continue;
|
||||
if (!s.rasterizer.isOccluded(
|
||||
cd.getMinX(i), cd.getMinY(i), cd.getMinZ(i),
|
||||
cd.getMaxX(i), cd.getMaxY(i), cd.getMaxZ(i),
|
||||
s.vp, s.rasterizer.width, s.rasterizer.height
|
||||
)) {
|
||||
meshes[i].shadowOcclusionVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
if (slot.flags.length < meshes.length) slot.flags.resize(meshes.length);
|
||||
slot.flagsLen = meshes.length;
|
||||
for (i in 0...meshes.length) slot.flags[i] = meshes[i].shadowOcclusionVisible;
|
||||
|
||||
#if lnx_csm
|
||||
if (isCsmLight && callIdx > 0) {
|
||||
var head = slots[0];
|
||||
var vp0 = shadowVps.get(light);
|
||||
if (head.appliedRev == sceneRev && head.flagsLen > 0 &&
|
||||
head.vp != null && vp0 != null && matEquals(vp0, head.vp)) {
|
||||
var hn = head.flagsLen < meshes.length ? head.flagsLen : meshes.length;
|
||||
for (i in 0...hn) meshes[i].shadowOcclusionVisible = head.flags[i];
|
||||
} else {
|
||||
for (i in 0...meshes.length) meshes[i].shadowOcclusionVisible = true;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
||||
slot.sceneRev = sceneRev;
|
||||
slot.appliedRev = sceneRev;
|
||||
slot.lastUsed = frame;
|
||||
lastShadowSlot = slot;
|
||||
lastShadowFrame = frame;
|
||||
lastShadowCallIdx = callIdx;
|
||||
shadowFlagsValid = true;
|
||||
}
|
||||
|
||||
function selectOccluders(
|
||||
meshes: Array<iron.object.MeshObject>,
|
||||
camX: Float, camY: Float, camZ: Float
|
||||
) {
|
||||
var cd = cullData;
|
||||
selCount = 0;
|
||||
var minPx = minOccluderPx;
|
||||
var autoPx = autoOccluderMinScreenRatio * screenH;
|
||||
for (k in 0...inFrustumList.length) {
|
||||
var i = inFrustumList[k];
|
||||
if (i >= meshes.length) continue;
|
||||
if (!meshes[i].occlusionVisible) continue;
|
||||
var flags = cd.getFlags(i);
|
||||
if ((flags & 16) != 0) continue;
|
||||
if ((flags & 32) != 0) continue;
|
||||
var r = cd.getRadius(i);
|
||||
var px: Float;
|
||||
var camInside = false;
|
||||
if (focalLengthPx > 0) {
|
||||
var dx = cd.getCenterX(i) - camX;
|
||||
var dy = cd.getCenterY(i) - camY;
|
||||
var dz = cd.getCenterZ(i) - camZ;
|
||||
var dist2 = dx * dx + dy * dy + dz * dz;
|
||||
if (dist2 <= r * r) camInside = true;
|
||||
px = camInside ? 1e6 : focalLengthPx * r / Math.sqrt(dist2);
|
||||
} else {
|
||||
px = r * orthoPxPerUnit;
|
||||
}
|
||||
if (px < minPx) continue;
|
||||
if ((flags & 1) == 0 && px < autoPx) continue;
|
||||
if (camInside || !boxOccluders) {
|
||||
var p = uidToProxy.get(cd.getObjectID(i));
|
||||
if (p == null || p.triangleCount <= 0 || p.triangleCount > maxTrisPerProxy) continue;
|
||||
}
|
||||
insertTopK(i, px, maxOccluders);
|
||||
}
|
||||
}
|
||||
|
||||
function insertTopK(idx: Int, score: Float, k: Int) {
|
||||
if (selIdx.length < k + 1) {
|
||||
selIdx.resize(k + 1);
|
||||
selScore.resize(k + 1);
|
||||
}
|
||||
var pos = selCount;
|
||||
while (pos > 0 && selScore[pos - 1] < score) pos--;
|
||||
if (pos >= k) return;
|
||||
var last = selCount < k ? selCount : k - 1;
|
||||
var j = last;
|
||||
while (j > pos) {
|
||||
selIdx[j] = selIdx[j - 1];
|
||||
selScore[j] = selScore[j - 1];
|
||||
j--;
|
||||
}
|
||||
selIdx[pos] = idx;
|
||||
selScore[pos] = score;
|
||||
if (selCount < k) selCount++;
|
||||
}
|
||||
|
||||
function rasterizeSelected(meshes: Array<iron.object.MeshObject>, vp: Mat4) {
|
||||
var cd = cullData;
|
||||
var triBudget = maxOccluderTris;
|
||||
#if (target.threaded)
|
||||
rasterizer.beginCollect();
|
||||
#end
|
||||
for (s in 0...selCount) {
|
||||
if (triBudget < 12) break;
|
||||
var i = selIdx[s];
|
||||
var proxy = uidToProxy.get(cd.getObjectID(i));
|
||||
if (proxy != null && proxy.triangleCount > 0 &&
|
||||
proxy.triangleCount <= maxTrisPerProxy && proxy.triangleCount <= triBudget) {
|
||||
var mesh = i < meshes.length ? meshes[i] : null;
|
||||
if (mesh == null) continue;
|
||||
proxy.rasterize(rasterizer, mesh.transform.world, vp, screenW, screenH);
|
||||
triBudget -= proxy.triangleCount;
|
||||
} else {
|
||||
if (!boxOccluders) continue;
|
||||
rasterizeBoxOccluder(rasterizer, i, vp);
|
||||
triBudget -= 12;
|
||||
}
|
||||
}
|
||||
#if (target.threaded)
|
||||
rasterizer.flushCollected();
|
||||
#end
|
||||
}
|
||||
|
||||
function rasterizeBoxOccluder(raster: SoftwareOcclusionRasterizer, objectIndex: Int, vp: Mat4, shrinkOverride = -1.0) {
|
||||
var cd = cullData;
|
||||
var bx0 = cd.getMinX(objectIndex), by0 = cd.getMinY(objectIndex), bz0 = cd.getMinZ(objectIndex);
|
||||
var bx1 = cd.getMaxX(objectIndex), by1 = cd.getMaxY(objectIndex), bz1 = cd.getMaxZ(objectIndex);
|
||||
var cx = (bx0 + bx1) * 0.5;
|
||||
var cy = (by0 + by1) * 0.5;
|
||||
var cz = (bz0 + bz1) * 0.5;
|
||||
var boxShrink = shrinkOverride >= 0.0 ? shrinkOverride : CullSystem.boxShrink;
|
||||
var hx = (bx1 - bx0) * 0.5 * boxShrink;
|
||||
var hy = (by1 - by0) * 0.5 * boxShrink;
|
||||
var hz = (bz1 - bz0) * 0.5 * boxShrink;
|
||||
raster.rasterizeBox(
|
||||
cx - hx, cy - hy, cz - hz,
|
||||
cx + hx, cy + hy, cz + hz,
|
||||
vp, raster.width, raster.height
|
||||
);
|
||||
}
|
||||
|
||||
function testVisibilityBVH(
|
||||
vp: Mat4, camX: Float, camY: Float, camZ: Float,
|
||||
planes: Array<FrustumPlane>
|
||||
) {
|
||||
var cd = cullData;
|
||||
var raster = rasterizer;
|
||||
var sw = screenW;
|
||||
var sh = screenH;
|
||||
var meshes = cd.meshes;
|
||||
var bvh = this.bvh;
|
||||
var temp = temporal;
|
||||
var retestInterval = 4;
|
||||
|
||||
bvh.traverseFrontToBack(camX, camY, camZ,
|
||||
function(node: BVHNode): Bool {
|
||||
if (!ObjectBVH.aabbInFrustum(node, planes)) return true;
|
||||
var anyVisible = false;
|
||||
if (node.isLeaf) {
|
||||
for (j in 0...node.objectCount) {
|
||||
var idx = bvh.objectIndices[node.firstObject + j];
|
||||
if ((cd.getFlags(idx) & 8) != 0) { anyVisible = true; break; }
|
||||
}
|
||||
if (!anyVisible) return true;
|
||||
}
|
||||
if (raster.isOccluded(
|
||||
node.minX, node.minY, node.minZ,
|
||||
node.maxX, node.maxY, node.maxZ,
|
||||
vp, sw, sh
|
||||
)) {
|
||||
bvh.forEachLeafInSubtree(node, function(idx: Int) {
|
||||
if (idx >= meshes.length || (cd.getFlags(idx) & 8) == 0) return;
|
||||
meshes[idx].occlusionVisible = false;
|
||||
var uid = cd.getObjectID(idx);
|
||||
temp.setVisible(uid, false);
|
||||
temp.markTested(uid);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
function(objIdx: Int, _: Int) {
|
||||
if ((cd.getFlags(objIdx) & 8) == 0) return;
|
||||
if (objIdx >= meshes.length) return;
|
||||
var uid = cd.getObjectID(objIdx);
|
||||
if (temp.wasVisibleLastFrame(uid) && !temp.needsVisibleReTest(uid, retestInterval)) {
|
||||
meshes[objIdx].occlusionVisible = true;
|
||||
cd.addVisible(objIdx);
|
||||
return;
|
||||
}
|
||||
if (raster.isOccluded(
|
||||
cd.getMinX(objIdx), cd.getMinY(objIdx), cd.getMinZ(objIdx),
|
||||
cd.getMaxX(objIdx), cd.getMaxY(objIdx), cd.getMaxZ(objIdx),
|
||||
vp, sw, sh
|
||||
)) {
|
||||
meshes[objIdx].occlusionVisible = false;
|
||||
temp.setVisible(uid, false);
|
||||
temp.markTested(uid);
|
||||
} else {
|
||||
meshes[objIdx].occlusionVisible = true;
|
||||
cd.addVisible(objIdx);
|
||||
temp.setVisible(uid, true);
|
||||
temp.markTested(uid);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static var autoOccluderMinScreenRatio: Float = 0.10;
|
||||
|
||||
public inline function isVisible(objectIndex: Int): Bool {
|
||||
var ms = cullData.meshes;
|
||||
return objectIndex >= 0 && objectIndex < ms.length && ms[objectIndex].occlusionVisible;
|
||||
}
|
||||
|
||||
public inline function markDirty() {
|
||||
bvhDirty = true;
|
||||
proxiesDirty = true;
|
||||
cullData.slotsDirty = true;
|
||||
shadowFlagsValid = false;
|
||||
}
|
||||
|
||||
public function removeLight(light: iron.object.LightObject) {
|
||||
shadowPasses.remove(light);
|
||||
shadowCallIdx.remove(light);
|
||||
shadowCallFrame.remove(light);
|
||||
#if lnx_csm
|
||||
shadowVps.remove(light);
|
||||
#end
|
||||
}
|
||||
|
||||
public static function init() {
|
||||
if (active == null) {
|
||||
active = new CullSystem();
|
||||
}
|
||||
}
|
||||
|
||||
public function loadOccluderProxies() {
|
||||
occluderProxies.resize(0);
|
||||
if (Scene.active == null) {
|
||||
uidToProxy.clear();
|
||||
return;
|
||||
}
|
||||
var meshes = Scene.active.meshes;
|
||||
uidScan.clear();
|
||||
for (i in 0...meshes.length) {
|
||||
var mesh = meshes[i];
|
||||
uidScan.set(mesh.uid, true);
|
||||
if (uidToProxy.exists(mesh.uid)) continue;
|
||||
if (mesh.data == null || mesh.data.raw == null) continue;
|
||||
var raw = mesh.data.raw;
|
||||
if (raw.occluder_proxy == null) continue;
|
||||
var proxyRaw = raw.occluder_proxy;
|
||||
if (proxyRaw.positions == null || proxyRaw.indices == null) continue;
|
||||
var proxy = new OccluderProxy();
|
||||
proxy.setData(proxyRaw.positions, proxyRaw.indices, mesh.uid);
|
||||
uidToProxy.set(mesh.uid, proxy);
|
||||
}
|
||||
var dead: Array<Int> = null;
|
||||
for (uid in uidToProxy.keys()) {
|
||||
if (!uidScan.exists(uid)) {
|
||||
if (dead == null) dead = [];
|
||||
dead.push(uid);
|
||||
}
|
||||
}
|
||||
if (dead != null) for (uid in dead) uidToProxy.remove(uid);
|
||||
for (proxy in uidToProxy.iterator()) occluderProxies.push(proxy);
|
||||
}
|
||||
|
||||
public static function shutdown() {
|
||||
active = null;
|
||||
}
|
||||
}
|
||||
|
||||
class ShadowPassSlot {
|
||||
public var vp: Mat4 = null;
|
||||
public var rasterizer: SoftwareOcclusionRasterizer = null;
|
||||
public var sceneRev: Int = -1;
|
||||
public var appliedRev: Int = -1;
|
||||
public var lastUsed: Int = -1;
|
||||
public var flags: Array<Bool> = [];
|
||||
public var flagsLen: Int = 0;
|
||||
public function new() {}
|
||||
}
|
||||
9
leenkx/Sources/iron/culling/CullTier.hx
Normal file
@ -0,0 +1,9 @@
|
||||
package iron.culling;
|
||||
|
||||
enum CullTier {
|
||||
CPUSingleThread;
|
||||
CPUMultiThread;
|
||||
GPUCompute;
|
||||
GPUDriven;
|
||||
MultiGPU;
|
||||
}
|
||||
171
leenkx/Sources/iron/culling/HiZPyramid.hx
Normal file
@ -0,0 +1,171 @@
|
||||
package iron.culling;
|
||||
|
||||
class HiZPyramid {
|
||||
|
||||
public var width: Int;
|
||||
public var height: Int;
|
||||
public var mipLevels: Int;
|
||||
public var mipBuffers: Array<kha.arrays.Float32Array>;
|
||||
public var mipWidths: Array<Int>;
|
||||
public var mipHeights: Array<Int>;
|
||||
|
||||
public var depthEps: Float;
|
||||
|
||||
var mipShifts: Array<Int>;
|
||||
|
||||
public function new(width: Int, height: Int) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
mipBuffers = [];
|
||||
mipWidths = [];
|
||||
mipHeights = [];
|
||||
depthEps = CullSystem.depthEps;
|
||||
|
||||
var w = width;
|
||||
var h = height;
|
||||
mipLevels = 0;
|
||||
while (w > 1 || h > 1) {
|
||||
mipWidths.push(w);
|
||||
mipHeights.push(h);
|
||||
mipBuffers.push(new kha.arrays.Float32Array(w * h));
|
||||
mipLevels++;
|
||||
w = (w + 1) >> 1;
|
||||
h = (h + 1) >> 1;
|
||||
}
|
||||
mipWidths.push(1);
|
||||
mipHeights.push(1);
|
||||
mipBuffers.push(new kha.arrays.Float32Array(1));
|
||||
mipLevels++;
|
||||
|
||||
mipShifts = [0, 1, 2, 3, 4, 5, 6];
|
||||
while (mipShifts.length < mipLevels) mipShifts.push(mipShifts.length);
|
||||
}
|
||||
|
||||
public function clear(farDepth: Float = 1e20) {
|
||||
var buf = mipBuffers[0];
|
||||
for (i in 0...buf.length) buf[i] = farDepth;
|
||||
}
|
||||
|
||||
public inline function writeDepth(x: Int, y: Int, depth: Float) {
|
||||
var idx = y * width + x;
|
||||
if (x >= 0 && x < width && y >= 0 && y < height) {
|
||||
if (depth < mipBuffers[0][idx]) {
|
||||
mipBuffers[0][idx] = depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function buildPyramid() {
|
||||
for (mip in 1...mipLevels) {
|
||||
var prev = mipBuffers[mip - 1];
|
||||
var cur = mipBuffers[mip];
|
||||
var pw = mipWidths[mip - 1];
|
||||
var ph = mipHeights[mip - 1];
|
||||
var cw = mipWidths[mip];
|
||||
var ch = mipHeights[mip];
|
||||
|
||||
for (y in 0...ch) {
|
||||
for (x in 0...cw) {
|
||||
var sx = x * 2;
|
||||
var sy = y * 2;
|
||||
var d00 = prev[sy * pw + sx];
|
||||
var d10 = (sx + 1 < pw) ? prev[sy * pw + sx + 1] : d00;
|
||||
var d01 = (sy + 1 < ph) ? prev[(sy + 1) * pw + sx] : d00;
|
||||
var d11 = (sx + 1 < pw && sy + 1 < ph) ? prev[(sy + 1) * pw + sx + 1] : d00;
|
||||
var maxD = d00;
|
||||
if (d10 > maxD) maxD = d10;
|
||||
if (d01 > maxD) maxD = d01;
|
||||
if (d11 > maxD) maxD = d11;
|
||||
cur[y * cw + x] = maxD;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public inline function sample(x: Int, y: Int, mip: Int): Float {
|
||||
var mw = mipWidths[mip];
|
||||
var mh = mipHeights[mip];
|
||||
if (x < 0) x = 0;
|
||||
if (y < 0) y = 0;
|
||||
if (x >= mw) x = mw - 1;
|
||||
if (y >= mh) y = mh - 1;
|
||||
return mipBuffers[mip][y * mw + x];
|
||||
}
|
||||
|
||||
public static function mipForSize(rectW: Float, rectH: Float, maxMip: Int): Int {
|
||||
var maxDim = Math.max(rectW, rectH);
|
||||
if (maxDim <= 2.0) return 0;
|
||||
var mip = Std.int(Math.floor(Math.log(maxDim) / Math.log(2))) - 1;
|
||||
if (mip < 0) mip = 0;
|
||||
if (mip > maxMip) mip = maxMip;
|
||||
return mip;
|
||||
}
|
||||
|
||||
public function isOccluded(
|
||||
minX: Float, minY: Float, minZ: Float,
|
||||
maxX: Float, maxY: Float, maxZ: Float,
|
||||
vp: iron.math.Mat4,
|
||||
_screenW: Int, _screenH: Int
|
||||
): Bool {
|
||||
var minSx = 1e20, minSy = 1e20;
|
||||
var maxSx = -1e20, maxSy = -1e20;
|
||||
var minDepth = 1e20;
|
||||
|
||||
var m00 = vp._00, m10 = vp._10, m20 = vp._20, m30 = vp._30;
|
||||
var m01 = vp._01, m11 = vp._11, m21 = vp._21, m31 = vp._31;
|
||||
var m02 = vp._02, m12 = vp._12, m22 = vp._22, m32 = vp._32;
|
||||
var m03 = vp._03, m13 = vp._13, m23 = vp._23, m33 = vp._33;
|
||||
|
||||
for (c in 0...8) {
|
||||
var x = (c & 1) != 0 ? maxX : minX;
|
||||
var y = (c & 2) != 0 ? maxY : minY;
|
||||
var z = (c & 4) != 0 ? maxZ : minZ;
|
||||
var w = m03 * x + m13 * y + m23 * z + m33;
|
||||
if (w <= 0) return false;
|
||||
var invW = SimdOps.reciprocal(w);
|
||||
var ndcZ = (m02 * x + m12 * y + m22 * z + m32) * invW;
|
||||
if (ndcZ < -1.0) ndcZ = -1.0; else if (ndcZ > 1.0) ndcZ = 1.0;
|
||||
var sx = ((m00 * x + m10 * y + m20 * z + m30) * invW * 0.5 + 0.5) * width;
|
||||
var sy = ((m01 * x + m11 * y + m21 * z + m31) * invW * 0.5 + 0.5) * height;
|
||||
if (sx < minSx) minSx = sx;
|
||||
if (sy < minSy) minSy = sy;
|
||||
if (sx > maxSx) maxSx = sx;
|
||||
if (sy > maxSy) maxSy = sy;
|
||||
if (ndcZ < minDepth) minDepth = ndcZ;
|
||||
}
|
||||
|
||||
var ix0 = Std.int(Math.max(0, minSx));
|
||||
var iy0 = Std.int(Math.max(0, minSy));
|
||||
var ix1 = Std.int(Math.min(width - 1, maxSx));
|
||||
var iy1 = Std.int(Math.min(height - 1, maxSy));
|
||||
|
||||
if (ix0 > ix1 || iy0 > iy1) return false;
|
||||
|
||||
if (ix0 > 0) ix0--;
|
||||
if (iy0 > 0) iy0--;
|
||||
if (ix1 < width - 1) ix1++;
|
||||
if (iy1 < height - 1) iy1++;
|
||||
|
||||
var rectW = ix1 - ix0 + 1;
|
||||
var rectH = iy1 - iy0 + 1;
|
||||
var mip = mipForSize(rectW, rectH, mipLevels - 1);
|
||||
|
||||
var mw = mipWidths[mip];
|
||||
var mh = mipHeights[mip];
|
||||
var shift = mipShifts[mip];
|
||||
var tx0 = Std.int(Math.min(mw - 1, ix0 >> shift));
|
||||
var ty0 = Std.int(Math.min(mh - 1, iy0 >> shift));
|
||||
var tx1 = Std.int(Math.min(mw - 1, ix1 >> shift));
|
||||
var ty1 = Std.int(Math.min(mh - 1, iy1 >> shift));
|
||||
|
||||
var buf = mipBuffers[mip];
|
||||
var limit = minDepth - depthEps;
|
||||
for (ty in ty0...ty1 + 1) {
|
||||
var rowOff = ty * mw;
|
||||
for (tx in tx0...tx1 + 1) {
|
||||
if (buf[rowOff + tx] >= limit) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
60
leenkx/Sources/iron/culling/JobSystem.hx
Normal file
@ -0,0 +1,60 @@
|
||||
package iron.culling;
|
||||
|
||||
#if (target.threaded)
|
||||
|
||||
class JobSystem {
|
||||
|
||||
public static var numWorkers(default, null): Int = 0;
|
||||
|
||||
static var jobQueue: sys.thread.Deque<Void->Void> = null;
|
||||
static var doneLock: sys.thread.Lock = null;
|
||||
static var jobError: Dynamic = null;
|
||||
|
||||
public static function init(workers: Int = 3) {
|
||||
if (jobQueue != null) return;
|
||||
numWorkers = workers < 1 ? 1 : workers;
|
||||
jobQueue = new sys.thread.Deque();
|
||||
doneLock = new sys.thread.Lock();
|
||||
for (_ in 0...numWorkers) {
|
||||
sys.thread.Thread.create(workerLoop);
|
||||
}
|
||||
}
|
||||
|
||||
static function workerLoop() {
|
||||
while (true) {
|
||||
var job = jobQueue.pop(true);
|
||||
if (job == null) return;
|
||||
try {
|
||||
job();
|
||||
} catch (e: Dynamic) {
|
||||
jobError = e;
|
||||
}
|
||||
doneLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
public static function parallelFor(count: Int, fn: Int -> Int -> Void) {
|
||||
init();
|
||||
var chunks = numWorkers + 1;
|
||||
var chunkSize = Std.int((count + chunks - 1) / chunks);
|
||||
if (chunkSize < 1) chunkSize = 1;
|
||||
var jobs = 0;
|
||||
var start = chunkSize; // Chunk 0 runs on the calling thread
|
||||
while (start < count) {
|
||||
var s = start;
|
||||
var e = start + chunkSize > count ? count : start + chunkSize;
|
||||
jobQueue.add(function() fn(s, e));
|
||||
jobs++;
|
||||
start = e;
|
||||
}
|
||||
fn(0, chunkSize > count ? count : chunkSize);
|
||||
for (_ in 0...jobs) doneLock.wait();
|
||||
if (jobError != null) {
|
||||
var e = jobError;
|
||||
jobError = null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#end
|
||||
376
leenkx/Sources/iron/culling/ObjectBVH.hx
Normal file
@ -0,0 +1,376 @@
|
||||
package iron.culling;
|
||||
|
||||
class ObjectBVH {
|
||||
|
||||
static inline var LEAF_THRESHOLD = 4;
|
||||
static inline var NUM_BINS = 12;
|
||||
static inline var TRAVERSAL_COST = 1.0;
|
||||
static inline var INTERSECT_COST = 1.0;
|
||||
|
||||
public var nodes: Array<BVHNode>;
|
||||
public var objectIndices: Array<Int>;
|
||||
public var root: Int = -1;
|
||||
public var objectCount: Int = 0;
|
||||
|
||||
var stack: Array<Int> = [];
|
||||
|
||||
static var binCounts: Array<Int> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMinX: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMinY: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMinZ: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMaxX: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMaxY: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
static var binMaxZ: Array<Float> = [for (i in 0...NUM_BINS) 0];
|
||||
|
||||
public function new() {
|
||||
nodes = [];
|
||||
objectIndices = [];
|
||||
}
|
||||
|
||||
public function build(cullData: CullData) {
|
||||
objectCount = cullData.count;
|
||||
nodes = [];
|
||||
objectIndices = [];
|
||||
|
||||
if (objectCount == 0) {
|
||||
root = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
var indices = [for (i in 0...objectCount) i];
|
||||
root = buildRecursive(indices, cullData);
|
||||
}
|
||||
|
||||
static inline function surfaceArea(minX: Float, minY: Float, minZ: Float,
|
||||
maxX: Float, maxY: Float, maxZ: Float): Float {
|
||||
var wx = maxX - minX;
|
||||
var wy = maxY - minY;
|
||||
var wz = maxZ - minZ;
|
||||
return 2.0 * (wx * wy + wx * wz + wy * wz);
|
||||
}
|
||||
|
||||
function buildRecursive(indices: Array<Int>, cullData: CullData): Int {
|
||||
var nodeIdx = nodes.length;
|
||||
var node = new BVHNode();
|
||||
nodes.push(node);
|
||||
|
||||
var minX = 1e20, minY = 1e20, minZ = 1e20;
|
||||
var maxX = -1e20, maxY = -1e20, maxZ = -1e20;
|
||||
var centMinX = 1e20, centMinY = 1e20, centMinZ = 1e20;
|
||||
var centMaxX = -1e20, centMaxY = -1e20, centMaxZ = -1e20;
|
||||
var cx = 0.0, cy = 0.0, cz = 0.0;
|
||||
|
||||
for (idx in indices) {
|
||||
var x = cullData.getCenterX(idx);
|
||||
var y = cullData.getCenterY(idx);
|
||||
var z = cullData.getCenterZ(idx);
|
||||
if (cullData.getMinX(idx) < minX) minX = cullData.getMinX(idx);
|
||||
if (cullData.getMinY(idx) < minY) minY = cullData.getMinY(idx);
|
||||
if (cullData.getMinZ(idx) < minZ) minZ = cullData.getMinZ(idx);
|
||||
if (cullData.getMaxX(idx) > maxX) maxX = cullData.getMaxX(idx);
|
||||
if (cullData.getMaxY(idx) > maxY) maxY = cullData.getMaxY(idx);
|
||||
if (cullData.getMaxZ(idx) > maxZ) maxZ = cullData.getMaxZ(idx);
|
||||
if (x < centMinX) centMinX = x;
|
||||
if (y < centMinY) centMinY = y;
|
||||
if (z < centMinZ) centMinZ = z;
|
||||
if (x > centMaxX) centMaxX = x;
|
||||
if (y > centMaxY) centMaxY = y;
|
||||
if (z > centMaxZ) centMaxZ = z;
|
||||
cx += x;
|
||||
cy += y;
|
||||
cz += z;
|
||||
}
|
||||
|
||||
var n = indices.length;
|
||||
cx /= n;
|
||||
cy /= n;
|
||||
cz /= n;
|
||||
|
||||
node.minX = minX; node.minY = minY; node.minZ = minZ;
|
||||
node.maxX = maxX; node.maxY = maxY; node.maxZ = maxZ;
|
||||
node.centerX = cx;
|
||||
node.centerY = cy;
|
||||
node.centerZ = cz;
|
||||
|
||||
if (n <= LEAF_THRESHOLD) {
|
||||
node.isLeaf = true;
|
||||
node.firstObject = objectIndices.length;
|
||||
for (idx in indices) objectIndices.push(idx);
|
||||
node.objectCount = n;
|
||||
return nodeIdx;
|
||||
}
|
||||
|
||||
var parentSA = surfaceArea(minX, minY, minZ, maxX, maxY, maxZ);
|
||||
var leafCost = INTERSECT_COST * n;
|
||||
|
||||
var bestCost = 1e20;
|
||||
var bestAxis = -1;
|
||||
var bestSplitBin = -1;
|
||||
|
||||
var centBounds = [
|
||||
[centMinX, centMaxX],
|
||||
[centMinY, centMaxY],
|
||||
[centMinZ, centMaxZ]
|
||||
];
|
||||
|
||||
for (axis in 0...3) {
|
||||
var lo = centBounds[axis][0];
|
||||
var hi = centBounds[axis][1];
|
||||
var range = hi - lo;
|
||||
if (range < 1e-8) continue;
|
||||
|
||||
var invRange = NUM_BINS / range;
|
||||
|
||||
for (b in 0...NUM_BINS) {
|
||||
binCounts[b] = 0;
|
||||
binMinX[b] = 1e20; binMinY[b] = 1e20; binMinZ[b] = 1e20;
|
||||
binMaxX[b] = -1e20; binMaxY[b] = -1e20; binMaxZ[b] = -1e20;
|
||||
}
|
||||
|
||||
for (idx in indices) {
|
||||
var cv = axis == 0 ? cullData.getCenterX(idx) :
|
||||
axis == 1 ? cullData.getCenterY(idx) :
|
||||
cullData.getCenterZ(idx);
|
||||
var b = Std.int((cv - lo) * invRange);
|
||||
if (b < 0) b = 0;
|
||||
if (b >= NUM_BINS) b = NUM_BINS - 1;
|
||||
binCounts[b]++;
|
||||
if (cullData.getMinX(idx) < binMinX[b]) binMinX[b] = cullData.getMinX(idx);
|
||||
if (cullData.getMinY(idx) < binMinY[b]) binMinY[b] = cullData.getMinY(idx);
|
||||
if (cullData.getMinZ(idx) < binMinZ[b]) binMinZ[b] = cullData.getMinZ(idx);
|
||||
if (cullData.getMaxX(idx) > binMaxX[b]) binMaxX[b] = cullData.getMaxX(idx);
|
||||
if (cullData.getMaxY(idx) > binMaxY[b]) binMaxY[b] = cullData.getMaxY(idx);
|
||||
if (cullData.getMaxZ(idx) > binMaxZ[b]) binMaxZ[b] = cullData.getMaxZ(idx);
|
||||
}
|
||||
|
||||
var rightCount = [for (b in 0...NUM_BINS) 0];
|
||||
var rightMinX = [for (b in 0...NUM_BINS) 0.0];
|
||||
var rightMinY = [for (b in 0...NUM_BINS) 0.0];
|
||||
var rightMinZ = [for (b in 0...NUM_BINS) 0.0];
|
||||
var rightMaxX = [for (b in 0...NUM_BINS) 0.0];
|
||||
var rightMaxY = [for (b in 0...NUM_BINS) 0.0];
|
||||
var rightMaxZ = [for (b in 0...NUM_BINS) 0.0];
|
||||
|
||||
var cumCount = 0;
|
||||
var cumMinX = 1e20, cumMinY = 1e20, cumMinZ = 1e20;
|
||||
var cumMaxX = -1e20, cumMaxY = -1e20, cumMaxZ = -1e20;
|
||||
|
||||
for (b in 0...NUM_BINS) {
|
||||
var bi = NUM_BINS - 1 - b;
|
||||
cumCount += binCounts[bi];
|
||||
if (binMinX[bi] < cumMinX) cumMinX = binMinX[bi];
|
||||
if (binMinY[bi] < cumMinY) cumMinY = binMinY[bi];
|
||||
if (binMinZ[bi] < cumMinZ) cumMinZ = binMinZ[bi];
|
||||
if (binMaxX[bi] > cumMaxX) cumMaxX = binMaxX[bi];
|
||||
if (binMaxY[bi] > cumMaxY) cumMaxY = binMaxY[bi];
|
||||
if (binMaxZ[bi] > cumMaxZ) cumMaxZ = binMaxZ[bi];
|
||||
rightCount[bi] = cumCount;
|
||||
rightMinX[bi] = cumMinX; rightMinY[bi] = cumMinY; rightMinZ[bi] = cumMinZ;
|
||||
rightMaxX[bi] = cumMaxX; rightMaxY[bi] = cumMaxY; rightMaxZ[bi] = cumMaxZ;
|
||||
}
|
||||
|
||||
var leftCount = 0;
|
||||
var leftMinX = 1e20, leftMinY = 1e20, leftMinZ = 1e20;
|
||||
var leftMaxX = -1e20, leftMaxY = -1e20, leftMaxZ = -1e20;
|
||||
|
||||
for (b in 0...NUM_BINS - 1) {
|
||||
if (binCounts[b] == 0) continue;
|
||||
leftCount += binCounts[b];
|
||||
if (binMinX[b] < leftMinX) leftMinX = binMinX[b];
|
||||
if (binMinY[b] < leftMinY) leftMinY = binMinY[b];
|
||||
if (binMinZ[b] < leftMinZ) leftMinZ = binMinZ[b];
|
||||
if (binMaxX[b] > leftMaxX) leftMaxX = binMaxX[b];
|
||||
if (binMaxY[b] > leftMaxY) leftMaxY = binMaxY[b];
|
||||
if (binMaxZ[b] > leftMaxZ) leftMaxZ = binMaxZ[b];
|
||||
|
||||
var rc = rightCount[b + 1];
|
||||
if (rc == 0) continue;
|
||||
|
||||
var leftSA = surfaceArea(leftMinX, leftMinY, leftMinZ, leftMaxX, leftMaxY, leftMaxZ);
|
||||
var rightSA = surfaceArea(rightMinX[b + 1], rightMinY[b + 1], rightMinZ[b + 1],
|
||||
rightMaxX[b + 1], rightMaxY[b + 1], rightMaxZ[b + 1]);
|
||||
var cost = TRAVERSAL_COST + (leftSA * leftCount + rightSA * rc) / parentSA;
|
||||
|
||||
if (cost < bestCost) {
|
||||
bestCost = cost;
|
||||
bestAxis = axis;
|
||||
bestSplitBin = b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestAxis < 0 || bestCost >= leafCost) {
|
||||
node.isLeaf = true;
|
||||
node.firstObject = objectIndices.length;
|
||||
for (idx in indices) objectIndices.push(idx);
|
||||
node.objectCount = n;
|
||||
return nodeIdx;
|
||||
}
|
||||
|
||||
var lo = centBounds[bestAxis][0];
|
||||
var hi = centBounds[bestAxis][1];
|
||||
var invRange = NUM_BINS / (hi - lo);
|
||||
var splitBin = bestSplitBin;
|
||||
|
||||
var left = [];
|
||||
var right = [];
|
||||
for (idx in indices) {
|
||||
var cv = bestAxis == 0 ? cullData.getCenterX(idx) :
|
||||
bestAxis == 1 ? cullData.getCenterY(idx) :
|
||||
cullData.getCenterZ(idx);
|
||||
var b = Std.int((cv - lo) * invRange);
|
||||
if (b < 0) b = 0;
|
||||
if (b >= NUM_BINS) b = NUM_BINS - 1;
|
||||
if (b <= splitBin) left.push(idx);
|
||||
else right.push(idx);
|
||||
}
|
||||
|
||||
if (left.length == 0 || right.length == 0) {
|
||||
indices.sort(function(a, b): Int {
|
||||
var av = bestAxis == 0 ? cullData.getCenterX(a) :
|
||||
bestAxis == 1 ? cullData.getCenterY(a) :
|
||||
cullData.getCenterZ(a);
|
||||
var bv = bestAxis == 0 ? cullData.getCenterX(b) :
|
||||
bestAxis == 1 ? cullData.getCenterY(b) :
|
||||
cullData.getCenterZ(b);
|
||||
if (av < bv) return -1;
|
||||
if (av > bv) return 1;
|
||||
return 0;
|
||||
});
|
||||
var mid = n >> 1;
|
||||
left = indices.slice(0, mid);
|
||||
right = indices.slice(mid);
|
||||
}
|
||||
|
||||
node.isLeaf = false;
|
||||
node.leftChild = buildRecursive(left, cullData);
|
||||
node.rightChild = buildRecursive(right, cullData);
|
||||
|
||||
return nodeIdx;
|
||||
}
|
||||
|
||||
|
||||
public function refit(cullData: CullData) {
|
||||
var i = nodes.length - 1;
|
||||
while (i >= 0) {
|
||||
var node = nodes[i];
|
||||
if (node.isLeaf) {
|
||||
var minX = 1e20, minY = 1e20, minZ = 1e20;
|
||||
var maxX = -1e20, maxY = -1e20, maxZ = -1e20;
|
||||
for (j in 0...node.objectCount) {
|
||||
var idx = objectIndices[node.firstObject + j];
|
||||
if (cullData.getMinX(idx) < minX) minX = cullData.getMinX(idx);
|
||||
if (cullData.getMinY(idx) < minY) minY = cullData.getMinY(idx);
|
||||
if (cullData.getMinZ(idx) < minZ) minZ = cullData.getMinZ(idx);
|
||||
if (cullData.getMaxX(idx) > maxX) maxX = cullData.getMaxX(idx);
|
||||
if (cullData.getMaxY(idx) > maxY) maxY = cullData.getMaxY(idx);
|
||||
if (cullData.getMaxZ(idx) > maxZ) maxZ = cullData.getMaxZ(idx);
|
||||
}
|
||||
node.minX = minX; node.minY = minY; node.minZ = minZ;
|
||||
node.maxX = maxX; node.maxY = maxY; node.maxZ = maxZ;
|
||||
} else {
|
||||
var l = nodes[node.leftChild];
|
||||
var r = nodes[node.rightChild];
|
||||
node.minX = l.minX < r.minX ? l.minX : r.minX;
|
||||
node.minY = l.minY < r.minY ? l.minY : r.minY;
|
||||
node.minZ = l.minZ < r.minZ ? l.minZ : r.minZ;
|
||||
node.maxX = l.maxX > r.maxX ? l.maxX : r.maxX;
|
||||
node.maxY = l.maxY > r.maxY ? l.maxY : r.maxY;
|
||||
node.maxZ = l.maxZ > r.maxZ ? l.maxZ : r.maxZ;
|
||||
}
|
||||
node.centerX = (node.minX + node.maxX) * 0.5;
|
||||
node.centerY = (node.minY + node.maxY) * 0.5;
|
||||
node.centerZ = (node.minZ + node.maxZ) * 0.5;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
public function forEachLeafInSubtree(node: BVHNode, cb: (Int) -> Void) {
|
||||
if (node.isLeaf) {
|
||||
for (i in 0...node.objectCount) {
|
||||
cb(objectIndices[node.firstObject + i]);
|
||||
}
|
||||
} else {
|
||||
forEachLeafInSubtree(nodes[node.leftChild], cb);
|
||||
forEachLeafInSubtree(nodes[node.rightChild], cb);
|
||||
}
|
||||
}
|
||||
|
||||
public function traverseFrontToBack(
|
||||
camX: Float, camY: Float, camZ: Float,
|
||||
shouldSkip: (BVHNode) -> Bool,
|
||||
onLeaf: (Int, Int) -> Void
|
||||
) {
|
||||
if (root < 0) return;
|
||||
|
||||
stack.resize(0);
|
||||
stack.push(root);
|
||||
|
||||
while (stack.length > 0) {
|
||||
var nodeIdx = stack.pop();
|
||||
var node = nodes[nodeIdx];
|
||||
|
||||
if (shouldSkip(node)) continue;
|
||||
|
||||
if (node.isLeaf) {
|
||||
var start = node.firstObject;
|
||||
for (i in 0...node.objectCount) {
|
||||
onLeaf(objectIndices[start + i], i);
|
||||
}
|
||||
} else {
|
||||
var leftNode = nodes[node.leftChild];
|
||||
var rightNode = nodes[node.rightChild];
|
||||
var leftDist = distSq(leftNode.centerX, leftNode.centerY, leftNode.centerZ, camX, camY, camZ);
|
||||
var rightDist = distSq(rightNode.centerX, rightNode.centerY, rightNode.centerZ, camX, camY, camZ);
|
||||
|
||||
if (leftDist > rightDist) {
|
||||
stack.push(node.leftChild);
|
||||
stack.push(node.rightChild);
|
||||
} else {
|
||||
stack.push(node.rightChild);
|
||||
stack.push(node.leftChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline function distSq(ax: Float, ay: Float, az: Float, bx: Float, by: Float, bz: Float): Float {
|
||||
var dx = ax - bx;
|
||||
var dy = ay - by;
|
||||
var dz = az - bz;
|
||||
return dx * dx + dy * dy + dz * dz;
|
||||
}
|
||||
|
||||
public static function aabbInFrustumRaw(
|
||||
minX: Float, minY: Float, minZ: Float,
|
||||
maxX: Float, maxY: Float, maxZ: Float,
|
||||
planes: Array<iron.object.FrustumPlane>
|
||||
): Bool {
|
||||
for (i in 0...planes.length) {
|
||||
var plane = planes[i];
|
||||
var px = plane.normal.x >= 0 ? maxX : minX;
|
||||
var py = plane.normal.y >= 0 ? maxY : minY;
|
||||
var pz = plane.normal.z >= 0 ? maxZ : minZ;
|
||||
if (SimdOps.dot3(px, py, pz, plane.normal.x, plane.normal.y, plane.normal.z) + plane.constant < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function aabbInFrustum(
|
||||
node: BVHNode,
|
||||
planes: Array<iron.object.FrustumPlane>
|
||||
): Bool {
|
||||
for (i in 0...planes.length) {
|
||||
var plane = planes[i];
|
||||
var px = plane.normal.x >= 0 ? node.maxX : node.minX;
|
||||
var py = plane.normal.y >= 0 ? node.maxY : node.minY;
|
||||
var pz = plane.normal.z >= 0 ? node.maxZ : node.minZ;
|
||||
if (SimdOps.dot3(px, py, pz, plane.normal.x, plane.normal.y, plane.normal.z) + plane.constant < 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
71
leenkx/Sources/iron/culling/OccluderProxy.hx
Normal file
@ -0,0 +1,71 @@
|
||||
package iron.culling;
|
||||
|
||||
class OccluderProxy {
|
||||
|
||||
public var positions: kha.arrays.Float32Array;
|
||||
public var indices: kha.arrays.Uint32Array;
|
||||
public var triangleCount: Int;
|
||||
public var meshUid: Int;
|
||||
|
||||
var worldPositions: kha.arrays.Float32Array;
|
||||
var mvp: iron.math.Mat4;
|
||||
|
||||
public function new() {
|
||||
indices = new kha.arrays.Uint32Array(0);
|
||||
triangleCount = 0;
|
||||
}
|
||||
|
||||
public function setData(pos: kha.arrays.Float32Array, idx: kha.arrays.Uint32Array, uid: Int) {
|
||||
#if js
|
||||
if (!Std.isOfType(pos, js.lib.DataView)) {
|
||||
var srcF: Array<Float> = cast pos;
|
||||
var dstF = new kha.arrays.Float32Array(srcF.length);
|
||||
for (i in 0...srcF.length) dstF[i] = srcF[i];
|
||||
pos = dstF;
|
||||
}
|
||||
if (!Std.isOfType(idx, js.lib.DataView)) {
|
||||
var srcI: Array<Int> = cast idx;
|
||||
var dstI = new kha.arrays.Uint32Array(srcI.length);
|
||||
for (i in 0...srcI.length) dstI[i] = srcI[i];
|
||||
idx = dstI;
|
||||
}
|
||||
#end
|
||||
positions = pos;
|
||||
indices = idx;
|
||||
triangleCount = Std.int(idx.length / 3);
|
||||
meshUid = uid;
|
||||
}
|
||||
|
||||
public function rasterize(
|
||||
rasterizer: SoftwareOcclusionRasterizer,
|
||||
world: iron.math.Mat4,
|
||||
vp: iron.math.Mat4,
|
||||
screenW: Int, screenH: Int
|
||||
) {
|
||||
if (positions == null || indices == null || triangleCount == 0) return;
|
||||
|
||||
var vertCount = Std.int(positions.length / 3);
|
||||
|
||||
var clipSize = vertCount * 4;
|
||||
if (worldPositions == null || worldPositions.length < clipSize) {
|
||||
worldPositions = new kha.arrays.Float32Array(clipSize);
|
||||
}
|
||||
|
||||
if (mvp == null) mvp = iron.math.Mat4.identity();
|
||||
mvp.multmats(vp, world);
|
||||
|
||||
for (i in 0...vertCount) {
|
||||
var x = positions[i * 3];
|
||||
var y = positions[i * 3 + 1];
|
||||
var z = positions[i * 3 + 2];
|
||||
var off = i * 4;
|
||||
worldPositions[off] = mvp._00 * x + mvp._10 * y + mvp._20 * z + mvp._30;
|
||||
worldPositions[off + 1] = mvp._01 * x + mvp._11 * y + mvp._21 * z + mvp._31;
|
||||
worldPositions[off + 2] = mvp._02 * x + mvp._12 * y + mvp._22 * z + mvp._32;
|
||||
worldPositions[off + 3] = mvp._03 * x + mvp._13 * y + mvp._23 * z + mvp._33;
|
||||
}
|
||||
|
||||
rasterizer.rasterizeProxyTransformed(worldPositions, indices, screenW, screenH);
|
||||
}
|
||||
|
||||
}
|
||||
83
leenkx/Sources/iron/culling/SimdOps.hx
Normal file
@ -0,0 +1,83 @@
|
||||
package iron.culling;
|
||||
|
||||
#if cpp
|
||||
@:include("smmintrin.h")
|
||||
@:include("emmintrin.h")
|
||||
@:include("xmmintrin.h")
|
||||
#end
|
||||
class SimdOps {
|
||||
|
||||
public static inline function dot4(
|
||||
a0: Float, a1: Float, a2: Float, a3: Float,
|
||||
b0: Float, b1: Float, b2: Float, b3: Float
|
||||
): Float {
|
||||
#if cpp
|
||||
return untyped __cpp__("_mm_cvtss_f32(_mm_dp_ps(_mm_setr_ps({0}, {1}, {2}, {3}), _mm_setr_ps({4}, {5}, {6}, {7}), 0xF1))",
|
||||
a0, a1, a2, a3, b0, b1, b2, b3);
|
||||
#else
|
||||
return a0 * b0 + a1 * b1 + a2 * b2 + a3 * b3;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function dot3(
|
||||
a0: Float, a1: Float, a2: Float,
|
||||
b0: Float, b1: Float, b2: Float
|
||||
): Float {
|
||||
#if cpp
|
||||
return untyped __cpp__("_mm_cvtss_f32(_mm_dp_ps(_mm_setr_ps({0}, {1}, {2}, 0.0f), _mm_setr_ps({3}, {4}, {5}, 0.0f), 0x71))",
|
||||
a0, a1, a2, b0, b1, b2);
|
||||
#else
|
||||
return a0 * b0 + a1 * b1 + a2 * b2;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function reciprocal(x: Float): Float {
|
||||
#if cpp
|
||||
return untyped __cpp__("({ auto _r = _mm_rcp_ss(_mm_set_ss({0})); _mm_cvtss_f32(_mm_add_ss(_mm_mul_ss(_r, _mm_sub_ss(_mm_set_ss(2.0f), _mm_mul_ss(_r, _mm_set_ss({0})))), _r)); })", x);
|
||||
#else
|
||||
return 1.0 / x;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function max4f(
|
||||
a: Float, b: Float, c: Float, d: Float
|
||||
): Float {
|
||||
#if cpp
|
||||
return untyped __cpp__("_mm_cvtss_f32(_mm_max_ss(_mm_max_ss(_mm_set_ss({0}), _mm_set_ss({1})), _mm_max_ss(_mm_set_ss({2}), _mm_set_ss({3}))))",
|
||||
a, b, c, d);
|
||||
#else
|
||||
var m = a;
|
||||
if (b > m) m = b;
|
||||
if (c > m) m = c;
|
||||
if (d > m) m = d;
|
||||
return m;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function min4f(
|
||||
a: Float, b: Float, c: Float, d: Float
|
||||
): Float {
|
||||
#if cpp
|
||||
return untyped __cpp__("_mm_cvtss_f32(_mm_min_ss(_mm_min_ss(_mm_set_ss({0}), _mm_set_ss({1})), _mm_min_ss(_mm_set_ss({2}), _mm_set_ss({3}))))",
|
||||
a, b, c, d);
|
||||
#else
|
||||
var m = a;
|
||||
if (b < m) m = b;
|
||||
if (c < m) m = c;
|
||||
if (d < m) m = d;
|
||||
return m;
|
||||
#end
|
||||
}
|
||||
|
||||
public static inline function frustumSphereTest6(
|
||||
planeData: kha.arrays.Float32Array,
|
||||
cx: Float, cy: Float, cz: Float, radius: Float
|
||||
): Bool {
|
||||
for (i in 0...6) {
|
||||
var base = i * 4;
|
||||
var dist = dot3(cx, cy, cz, planeData[base], planeData[base + 1], planeData[base + 2]) + planeData[base + 3];
|
||||
if (dist + radius < 0) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
507
leenkx/Sources/iron/culling/SoftwareOcclusionRasterizer.hx
Normal file
@ -0,0 +1,507 @@
|
||||
package iron.culling;
|
||||
|
||||
class SoftwareOcclusionRasterizer {
|
||||
|
||||
public var hiz: HiZPyramid;
|
||||
public var width: Int;
|
||||
public var height: Int;
|
||||
|
||||
public var nearClip: Float = 0.0001;
|
||||
|
||||
static inline var TILE_SIZE = 32;
|
||||
|
||||
public var trianglesRasterized: Int = 0;
|
||||
|
||||
public var collectMode = false;
|
||||
var screenTris: Array<Float> = [];
|
||||
var screenTriCount = 0;
|
||||
|
||||
static final BOX_FACES = [
|
||||
[0, 1, 3, 2], // -Z
|
||||
[4, 5, 7, 6], // +Z
|
||||
[0, 1, 5, 4], // -Y
|
||||
[2, 3, 7, 6], // +Y
|
||||
[0, 2, 6, 4], // -X
|
||||
[1, 3, 7, 5], // +X
|
||||
];
|
||||
|
||||
var inX: Array<Float> = [0,0,0];
|
||||
var inY: Array<Float> = [0,0,0];
|
||||
var inZ: Array<Float> = [0,0,0];
|
||||
var inW: Array<Float> = [0,0,0];
|
||||
var outX: Array<Float> = [0,0,0,0];
|
||||
var outY: Array<Float> = [0,0,0,0];
|
||||
var outZ: Array<Float> = [0,0,0,0];
|
||||
var outW: Array<Float> = [0,0,0,0];
|
||||
var projSx: Array<Float> = [0,0,0,0];
|
||||
var projSy: Array<Float> = [0,0,0,0];
|
||||
var projSz: Array<Float> = [0,0,0,0];
|
||||
|
||||
var boxClipX: Array<Float> = [0,0,0,0,0,0,0,0];
|
||||
var boxClipY: Array<Float> = [0,0,0,0,0,0,0,0];
|
||||
var boxClipZ: Array<Float> = [0,0,0,0,0,0,0,0];
|
||||
var boxClipW: Array<Float> = [0,0,0,0,0,0,0,0];
|
||||
|
||||
public function new(width: Int = 256, height: Int = 128) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
hiz = new HiZPyramid(width, height);
|
||||
}
|
||||
|
||||
public function clear() {
|
||||
hiz.clear(1e20);
|
||||
trianglesRasterized = 0;
|
||||
}
|
||||
|
||||
public function rasterizeTriangle(
|
||||
v0x: Float, v0y: Float, v0z: Float,
|
||||
v1x: Float, v1y: Float, v1z: Float,
|
||||
v2x: Float, v2y: Float, v2z: Float
|
||||
) {
|
||||
var e0a = v1y - v0y; var e0b = v0x - v1x;
|
||||
var e1a = v2y - v1y; var e1b = v1x - v2x;
|
||||
var e2a = v0y - v2y; var e2b = v2x - v0x;
|
||||
var e0c = -e0a * v0x - e0b * v0y;
|
||||
var e1c = -e1a * v1x - e1b * v1y;
|
||||
var e2c = -e2a * v2x - e2b * v2y;
|
||||
|
||||
var area = e0a * v2x + e0b * v2y + e0c;
|
||||
if (area < 0) {
|
||||
area = -area;
|
||||
e0a = -e0a; e0b = -e0b; e0c = -e0c;
|
||||
e1a = -e1a; e1b = -e1b; e1c = -e1c;
|
||||
e2a = -e2a; e2b = -e2b; e2c = -e2c;
|
||||
}
|
||||
if (area < 1e-8) return;
|
||||
|
||||
var minX = Std.int(Math.max(0, Math.floor(Math.min(v0x, Math.min(v1x, v2x)))));
|
||||
var maxX = Std.int(Math.min(width - 1, Math.ceil(Math.max(v0x, Math.max(v1x, v2x)))));
|
||||
var minY = Std.int(Math.max(0, Math.floor(Math.min(v0y, Math.min(v1y, v2y)))));
|
||||
var maxY = Std.int(Math.min(height - 1, Math.ceil(Math.max(v0y, Math.max(v1y, v2y)))));
|
||||
if (minX > maxX || minY > maxY) return;
|
||||
|
||||
var d = (v1x - v0x) * (v2y - v0y) - (v2x - v0x) * (v1y - v0y);
|
||||
var invD = 1.0 / d;
|
||||
var dzdx = ((v1z - v0z) * (v2y - v0y) - (v2z - v0z) * (v1y - v0y)) * invD;
|
||||
var dzdy = ((v2z - v0z) * (v1x - v0x) - (v1z - v0z) * (v2x - v0x)) * invD;
|
||||
|
||||
var zPush = (Math.abs(dzdx) + Math.abs(dzdy)) * 0.5;
|
||||
var zMaxTri = v0z;
|
||||
if (v1z > zMaxTri) zMaxTri = v1z;
|
||||
if (v2z > zMaxTri) zMaxTri = v2z;
|
||||
|
||||
var cx0 = minX + 0.5;
|
||||
var cy0 = minY + 0.5;
|
||||
var e0row = e0a * cx0 + e0b * cy0 + e0c;
|
||||
var e1row = e1a * cx0 + e1b * cy0 + e1c;
|
||||
var e2row = e2a * cx0 + e2b * cy0 + e2c;
|
||||
var zrow = v0z + dzdx * (cx0 - v0x) + dzdy * (cy0 - v0y);
|
||||
|
||||
var buf = hiz.mipBuffers[0];
|
||||
var w = width;
|
||||
for (py in minY...maxY + 1) {
|
||||
var e0 = e0row; var e1 = e1row; var e2 = e2row;
|
||||
var z = zrow;
|
||||
var idx = py * w + minX;
|
||||
for (px in minX...maxX + 1) {
|
||||
if (e0 >= 0 && e1 >= 0 && e2 >= 0) {
|
||||
var zPix = z + zPush;
|
||||
if (zPix > zMaxTri) zPix = zMaxTri;
|
||||
if (zPix < 0.0) zPix = 0.0;
|
||||
if (zPix > 1.0) zPix = 1.0;
|
||||
if (zPix < buf[idx]) buf[idx] = zPix;
|
||||
}
|
||||
e0 += e0a; e1 += e1a; e2 += e2a;
|
||||
z += dzdx;
|
||||
idx++;
|
||||
}
|
||||
e0row += e0b; e1row += e1b; e2row += e2b;
|
||||
zrow += dzdy;
|
||||
}
|
||||
}
|
||||
|
||||
function clipNearAndRasterize(
|
||||
cx0: Float, cy0: Float, cz0: Float, cw0: Float,
|
||||
cx1: Float, cy1: Float, cz1: Float, cw1: Float,
|
||||
cx2: Float, cy2: Float, cz2: Float, cw2: Float
|
||||
) {
|
||||
inX[0] = cx0; inY[0] = cy0; inZ[0] = cz0; inW[0] = cw0;
|
||||
inX[1] = cx1; inY[1] = cy1; inZ[1] = cz1; inW[1] = cw1;
|
||||
inX[2] = cx2; inY[2] = cy2; inZ[2] = cz2; inW[2] = cw2;
|
||||
|
||||
var outCount = 0;
|
||||
for (i in 0...3) {
|
||||
var aIdx = (i + 2) % 3;
|
||||
var bIdx = i;
|
||||
var aInside = inW[aIdx] >= nearClip;
|
||||
var bInside = inW[bIdx] >= nearClip;
|
||||
|
||||
if (aInside) {
|
||||
if (bInside) {
|
||||
outX[outCount] = inX[bIdx]; outY[outCount] = inY[bIdx];
|
||||
outZ[outCount] = inZ[bIdx]; outW[outCount] = inW[bIdx];
|
||||
outCount++;
|
||||
} else {
|
||||
var t = (inW[aIdx] - nearClip) / (inW[aIdx] - inW[bIdx]);
|
||||
outX[outCount] = inX[aIdx] + t * (inX[bIdx] - inX[aIdx]);
|
||||
outY[outCount] = inY[aIdx] + t * (inY[bIdx] - inY[aIdx]);
|
||||
outZ[outCount] = inZ[aIdx] + t * (inZ[bIdx] - inZ[aIdx]);
|
||||
outW[outCount] = inW[aIdx] + t * (inW[bIdx] - inW[aIdx]);
|
||||
outCount++;
|
||||
}
|
||||
} else {
|
||||
if (bInside) {
|
||||
var t = (inW[aIdx] - nearClip) / (inW[aIdx] - inW[bIdx]);
|
||||
outX[outCount] = inX[aIdx] + t * (inX[bIdx] - inX[aIdx]);
|
||||
outY[outCount] = inY[aIdx] + t * (inY[bIdx] - inY[aIdx]);
|
||||
outZ[outCount] = inZ[aIdx] + t * (inZ[bIdx] - inZ[aIdx]);
|
||||
outW[outCount] = inW[aIdx] + t * (inW[bIdx] - inW[aIdx]);
|
||||
outCount++;
|
||||
outX[outCount] = inX[bIdx]; outY[outCount] = inY[bIdx];
|
||||
outZ[outCount] = inZ[bIdx]; outW[outCount] = inW[bIdx];
|
||||
outCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (outCount < 3) return;
|
||||
|
||||
for (i in 0...outCount) {
|
||||
var invW = SimdOps.reciprocal(outW[i]);
|
||||
projSx[i] = (outX[i] * invW * 0.5 + 0.5) * width;
|
||||
projSy[i] = (outY[i] * invW * 0.5 + 0.5) * height;
|
||||
projSz[i] = outZ[i] * invW;
|
||||
}
|
||||
|
||||
var signedArea = (projSx[1] - projSx[0]) * (projSy[2] - projSy[0]) -
|
||||
(projSy[1] - projSy[0]) * (projSx[2] - projSx[0]);
|
||||
if (Math.abs(signedArea) < 1e-8) return;
|
||||
|
||||
trianglesRasterized++;
|
||||
|
||||
if (collectMode) {
|
||||
screenTris[screenTriCount++] = projSx[0]; screenTris[screenTriCount++] = projSy[0]; screenTris[screenTriCount++] = projSz[0];
|
||||
screenTris[screenTriCount++] = projSx[1]; screenTris[screenTriCount++] = projSy[1]; screenTris[screenTriCount++] = projSz[1];
|
||||
screenTris[screenTriCount++] = projSx[2]; screenTris[screenTriCount++] = projSy[2]; screenTris[screenTriCount++] = projSz[2];
|
||||
if (outCount == 4) {
|
||||
screenTris[screenTriCount++] = projSx[0]; screenTris[screenTriCount++] = projSy[0]; screenTris[screenTriCount++] = projSz[0];
|
||||
screenTris[screenTriCount++] = projSx[2]; screenTris[screenTriCount++] = projSy[2]; screenTris[screenTriCount++] = projSz[2];
|
||||
screenTris[screenTriCount++] = projSx[3]; screenTris[screenTriCount++] = projSy[3]; screenTris[screenTriCount++] = projSz[3];
|
||||
}
|
||||
} else {
|
||||
rasterizeTriangle(
|
||||
projSx[0], projSy[0], projSz[0],
|
||||
projSx[1], projSy[1], projSz[1],
|
||||
projSx[2], projSy[2], projSz[2]
|
||||
);
|
||||
if (outCount == 4) {
|
||||
rasterizeTriangle(
|
||||
projSx[0], projSy[0], projSz[0],
|
||||
projSx[2], projSy[2], projSz[2],
|
||||
projSx[3], projSy[3], projSz[3]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function rasterizeProxy(
|
||||
positions: kha.arrays.Float32Array,
|
||||
indices: kha.arrays.Uint32Array,
|
||||
vp: iron.math.Mat4,
|
||||
screenW: Int, screenH: Int
|
||||
) {
|
||||
var triCount = Std.int(indices.length / 3);
|
||||
|
||||
var m00 = vp._00, m10 = vp._10, m20 = vp._20, m30 = vp._30;
|
||||
var m01 = vp._01, m11 = vp._11, m21 = vp._21, m31 = vp._31;
|
||||
var m02 = vp._02, m12 = vp._12, m22 = vp._22, m32 = vp._32;
|
||||
var m03 = vp._03, m13 = vp._13, m23 = vp._23, m33 = vp._33;
|
||||
|
||||
for (t in 0...triCount) {
|
||||
var i0 = indices[t * 3] * 3;
|
||||
var i1 = indices[t * 3 + 1] * 3;
|
||||
var i2 = indices[t * 3 + 2] * 3;
|
||||
|
||||
var px0 = positions[i0], py0 = positions[i0 + 1], pz0 = positions[i0 + 2];
|
||||
var px1 = positions[i1], py1 = positions[i1 + 1], pz1 = positions[i1 + 2];
|
||||
var px2 = positions[i2], py2 = positions[i2 + 1], pz2 = positions[i2 + 2];
|
||||
|
||||
var cw0 = m03 * px0 + m13 * py0 + m23 * pz0 + m33;
|
||||
var cw1 = m03 * px1 + m13 * py1 + m23 * pz1 + m33;
|
||||
var cw2 = m03 * px2 + m13 * py2 + m23 * pz2 + m33;
|
||||
|
||||
if (cw0 < nearClip && cw1 < nearClip && cw2 < nearClip) continue;
|
||||
|
||||
var cx0 = m00 * px0 + m10 * py0 + m20 * pz0 + m30;
|
||||
var cy0 = m01 * px0 + m11 * py0 + m21 * pz0 + m31;
|
||||
var cz0 = m02 * px0 + m12 * py0 + m22 * pz0 + m32;
|
||||
|
||||
var cx1 = m00 * px1 + m10 * py1 + m20 * pz1 + m30;
|
||||
var cy1 = m01 * px1 + m11 * py1 + m21 * pz1 + m31;
|
||||
var cz1 = m02 * px1 + m12 * py1 + m22 * pz1 + m32;
|
||||
|
||||
var cx2 = m00 * px2 + m10 * py2 + m20 * pz2 + m30;
|
||||
var cy2 = m01 * px2 + m11 * py2 + m21 * pz2 + m31;
|
||||
var cz2 = m02 * px2 + m12 * py2 + m22 * pz2 + m32;
|
||||
|
||||
clipNearAndRasterize(
|
||||
cx0, cy0, cz0, cw0,
|
||||
cx1, cy1, cz1, cw1,
|
||||
cx2, cy2, cz2, cw2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function rasterizeProxyTransformed(
|
||||
clipPositions: kha.arrays.Float32Array,
|
||||
indices: kha.arrays.Uint32Array,
|
||||
screenW: Int, screenH: Int
|
||||
) {
|
||||
var triCount = Std.int(indices.length / 3);
|
||||
|
||||
for (t in 0...triCount) {
|
||||
var i0 = indices[t * 3] * 4;
|
||||
var i1 = indices[t * 3 + 1] * 4;
|
||||
var i2 = indices[t * 3 + 2] * 4;
|
||||
|
||||
var cx0 = clipPositions[i0], cy0 = clipPositions[i0 + 1], cz0 = clipPositions[i0 + 2], cw0 = clipPositions[i0 + 3];
|
||||
var cx1 = clipPositions[i1], cy1 = clipPositions[i1 + 1], cz1 = clipPositions[i1 + 2], cw1 = clipPositions[i1 + 3];
|
||||
var cx2 = clipPositions[i2], cy2 = clipPositions[i2 + 1], cz2 = clipPositions[i2 + 2], cw2 = clipPositions[i2 + 3];
|
||||
|
||||
if (cw0 < nearClip && cw1 < nearClip && cw2 < nearClip) continue;
|
||||
|
||||
clipNearAndRasterize(
|
||||
cx0, cy0, cz0, cw0,
|
||||
cx1, cy1, cz1, cw1,
|
||||
cx2, cy2, cz2, cw2
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function rasterizeBox(
|
||||
minX: Float, minY: Float, minZ: Float,
|
||||
maxX: Float, maxY: Float, maxZ: Float,
|
||||
vp: iron.math.Mat4,
|
||||
screenW: Int, screenH: Int
|
||||
) {
|
||||
var m00 = vp._00, m10 = vp._10, m20 = vp._20, m30 = vp._30;
|
||||
var m01 = vp._01, m11 = vp._11, m21 = vp._21, m31 = vp._31;
|
||||
var m02 = vp._02, m12 = vp._12, m22 = vp._22, m32 = vp._32;
|
||||
var m03 = vp._03, m13 = vp._13, m23 = vp._23, m33 = vp._33;
|
||||
|
||||
for (c in 0...8) {
|
||||
var x = (c & 1) != 0 ? maxX : minX;
|
||||
var y = (c & 2) != 0 ? maxY : minY;
|
||||
var z = (c & 4) != 0 ? maxZ : minZ;
|
||||
boxClipX[c] = m00 * x + m10 * y + m20 * z + m30;
|
||||
boxClipY[c] = m01 * x + m11 * y + m21 * z + m31;
|
||||
boxClipZ[c] = m02 * x + m12 * y + m22 * z + m32;
|
||||
boxClipW[c] = m03 * x + m13 * y + m23 * z + m33;
|
||||
}
|
||||
|
||||
for (face in BOX_FACES) {
|
||||
clipNearAndRasterize(
|
||||
boxClipX[face[0]], boxClipY[face[0]], boxClipZ[face[0]], boxClipW[face[0]],
|
||||
boxClipX[face[1]], boxClipY[face[1]], boxClipZ[face[1]], boxClipW[face[1]],
|
||||
boxClipX[face[2]], boxClipY[face[2]], boxClipZ[face[2]], boxClipW[face[2]]
|
||||
);
|
||||
clipNearAndRasterize(
|
||||
boxClipX[face[0]], boxClipY[face[0]], boxClipZ[face[0]], boxClipW[face[0]],
|
||||
boxClipX[face[2]], boxClipY[face[2]], boxClipZ[face[2]], boxClipW[face[2]],
|
||||
boxClipX[face[3]], boxClipY[face[3]], boxClipZ[face[3]], boxClipW[face[3]]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function buildHiZ() {
|
||||
hiz.buildPyramid();
|
||||
}
|
||||
|
||||
public function beginCollect() {
|
||||
collectMode = true;
|
||||
screenTriCount = 0;
|
||||
}
|
||||
|
||||
var tileTriCounts: Array<Int> = [];
|
||||
var tileTriOffsets: Array<Int> = [];
|
||||
var tileTriLists: Array<Array<Int>> = [];
|
||||
|
||||
public function flushCollected() {
|
||||
collectMode = false;
|
||||
var nTris = Std.int(screenTriCount / 9);
|
||||
if (nTris == 0) return;
|
||||
|
||||
var tilesX = Std.int(Math.ceil(width / TILE_SIZE));
|
||||
var tilesY = Std.int(Math.ceil(height / TILE_SIZE));
|
||||
var totalTiles = tilesX * tilesY;
|
||||
|
||||
if (tileTriCounts.length < totalTiles) {
|
||||
tileTriCounts.resize(totalTiles);
|
||||
tileTriOffsets.resize(totalTiles);
|
||||
tileTriLists.resize(totalTiles);
|
||||
}
|
||||
for (t in 0...totalTiles) tileTriCounts[t] = 0;
|
||||
for (t in 0...nTris) {
|
||||
var off = t * 9;
|
||||
var tx0 = Std.int(Math.max(0, Math.floor(Math.min(
|
||||
Math.min(screenTris[off], screenTris[off + 3]), screenTris[off + 6]) / TILE_SIZE)));
|
||||
var tx1 = Std.int(Math.min(tilesX - 1, Math.ceil(Math.max(
|
||||
Math.max(screenTris[off], screenTris[off + 3]), screenTris[off + 6]) / TILE_SIZE)));
|
||||
var ty0 = Std.int(Math.max(0, Math.floor(Math.min(
|
||||
Math.min(screenTris[off + 1], screenTris[off + 4]), screenTris[off + 7]) / TILE_SIZE)));
|
||||
var ty1 = Std.int(Math.min(tilesY - 1, Math.ceil(Math.max(
|
||||
Math.max(screenTris[off + 1], screenTris[off + 4]), screenTris[off + 7]) / TILE_SIZE)));
|
||||
for (ty in ty0...ty1 + 1) {
|
||||
for (tx in tx0...tx1 + 1) {
|
||||
tileTriCounts[ty * tilesX + tx]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (t in 0...totalTiles) {
|
||||
var l = tileTriLists[t];
|
||||
if (l == null) { l = []; tileTriLists[t] = l; }
|
||||
if (l.length < tileTriCounts[t]) l.resize(tileTriCounts[t]);
|
||||
tileTriOffsets[t] = 0;
|
||||
}
|
||||
for (t in 0...nTris) {
|
||||
var off = t * 9;
|
||||
var tx0 = Std.int(Math.max(0, Math.floor(Math.min(
|
||||
Math.min(screenTris[off], screenTris[off + 3]), screenTris[off + 6]) / TILE_SIZE)));
|
||||
var tx1 = Std.int(Math.min(tilesX - 1, Math.ceil(Math.max(
|
||||
Math.max(screenTris[off], screenTris[off + 3]), screenTris[off + 6]) / TILE_SIZE)));
|
||||
var ty0 = Std.int(Math.max(0, Math.floor(Math.min(
|
||||
Math.min(screenTris[off + 1], screenTris[off + 4]), screenTris[off + 7]) / TILE_SIZE)));
|
||||
var ty1 = Std.int(Math.min(tilesY - 1, Math.ceil(Math.max(
|
||||
Math.max(screenTris[off + 1], screenTris[off + 4]), screenTris[off + 7]) / TILE_SIZE)));
|
||||
for (ty in ty0...ty1 + 1) {
|
||||
for (tx in tx0...tx1 + 1) {
|
||||
var ti = ty * tilesX + tx;
|
||||
tileTriLists[ti][tileTriOffsets[ti]++] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var buf = hiz.mipBuffers[0];
|
||||
var w = width;
|
||||
var tris = screenTris;
|
||||
|
||||
#if (target.threaded)
|
||||
JobSystem.parallelFor(totalTiles, function(start, end) {
|
||||
for (ti in start...end) {
|
||||
var tx = ti % tilesX;
|
||||
var ty = Std.int(ti / tilesX);
|
||||
var px0 = tx * TILE_SIZE;
|
||||
var py0 = ty * TILE_SIZE;
|
||||
var px1 = Std.int(Math.min(w - 1, px0 + TILE_SIZE - 1));
|
||||
var py1 = Std.int(Math.min(height - 1, py0 + TILE_SIZE - 1));
|
||||
var list = tileTriLists[ti];
|
||||
var listLen = tileTriCounts[ti];
|
||||
for (li in 0...listLen) {
|
||||
var off = list[li] * 9;
|
||||
rasterizeTriangleBounded(
|
||||
tris[off], tris[off + 1], tris[off + 2],
|
||||
tris[off + 3], tris[off + 4], tris[off + 5],
|
||||
tris[off + 6], tris[off + 7], tris[off + 8],
|
||||
px0, py0, px1, py1, buf, w
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
#else
|
||||
for (ti in 0...totalTiles) {
|
||||
var tx = ti % tilesX;
|
||||
var ty = Std.int(ti / tilesX);
|
||||
var px0 = tx * TILE_SIZE;
|
||||
var py0 = ty * TILE_SIZE;
|
||||
var px1 = Std.int(Math.min(w - 1, px0 + TILE_SIZE - 1));
|
||||
var py1 = Std.int(Math.min(height - 1, py0 + TILE_SIZE - 1));
|
||||
var list = tileTriLists[ti];
|
||||
for (li in 0...list.length) {
|
||||
var off = list[li] * 9;
|
||||
rasterizeTriangleBounded(
|
||||
tris[off], tris[off + 1], tris[off + 2],
|
||||
tris[off + 3], tris[off + 4], tris[off + 5],
|
||||
tris[off + 6], tris[off + 7], tris[off + 8],
|
||||
px0, py0, px1, py1, buf, w
|
||||
);
|
||||
}
|
||||
}
|
||||
#end
|
||||
}
|
||||
|
||||
function rasterizeTriangleBounded(
|
||||
v0x: Float, v0y: Float, v0z: Float,
|
||||
v1x: Float, v1y: Float, v1z: Float,
|
||||
v2x: Float, v2y: Float, v2z: Float,
|
||||
tileX0: Int, tileY0: Int, tileX1: Int, tileY1: Int,
|
||||
buf: kha.arrays.Float32Array, w: Int
|
||||
) {
|
||||
var e0a = v1y - v0y; var e0b = v0x - v1x;
|
||||
var e1a = v2y - v1y; var e1b = v1x - v2x;
|
||||
var e2a = v0y - v2y; var e2b = v2x - v0x;
|
||||
var e0c = -e0a * v0x - e0b * v0y;
|
||||
var e1c = -e1a * v1x - e1b * v1y;
|
||||
var e2c = -e2a * v2x - e2b * v2y;
|
||||
|
||||
var area = e0a * v2x + e0b * v2y + e0c;
|
||||
if (area < 0) {
|
||||
area = -area;
|
||||
e0a = -e0a; e0b = -e0b; e0c = -e0c;
|
||||
e1a = -e1a; e1b = -e1b; e1c = -e1c;
|
||||
e2a = -e2a; e2b = -e2b; e2c = -e2c;
|
||||
}
|
||||
if (area < 1e-8) return;
|
||||
|
||||
var minX = Std.int(Math.max(tileX0, Math.floor(Math.min(v0x, Math.min(v1x, v2x)))));
|
||||
var maxX = Std.int(Math.min(tileX1, Math.ceil(Math.max(v0x, Math.max(v1x, v2x)))));
|
||||
var minY = Std.int(Math.max(tileY0, Math.floor(Math.min(v0y, Math.min(v1y, v2y)))));
|
||||
var maxY = Std.int(Math.min(tileY1, Math.ceil(Math.max(v0y, Math.max(v1y, v2y)))));
|
||||
if (minX > maxX || minY > maxY) return;
|
||||
|
||||
var d = (v1x - v0x) * (v2y - v0y) - (v2x - v0x) * (v1y - v0y);
|
||||
var invD = 1.0 / d;
|
||||
var dzdx = ((v1z - v0z) * (v2y - v0y) - (v2z - v0z) * (v1y - v0y)) * invD;
|
||||
var dzdy = ((v2z - v0z) * (v1x - v0x) - (v1z - v0z) * (v2x - v0x)) * invD;
|
||||
|
||||
var zPush = (Math.abs(dzdx) + Math.abs(dzdy)) * 0.5;
|
||||
var zMaxTri = v0z;
|
||||
if (v1z > zMaxTri) zMaxTri = v1z;
|
||||
if (v2z > zMaxTri) zMaxTri = v2z;
|
||||
|
||||
var cx0 = minX + 0.5;
|
||||
var cy0 = minY + 0.5;
|
||||
var e0row = e0a * cx0 + e0b * cy0 + e0c;
|
||||
var e1row = e1a * cx0 + e1b * cy0 + e1c;
|
||||
var e2row = e2a * cx0 + e2b * cy0 + e2c;
|
||||
var zrow = v0z + dzdx * (cx0 - v0x) + dzdy * (cy0 - v0y);
|
||||
|
||||
for (py in minY...maxY + 1) {
|
||||
var e0 = e0row; var e1 = e1row; var e2 = e2row;
|
||||
var z = zrow;
|
||||
var idx = py * w + minX;
|
||||
for (px in minX...maxX + 1) {
|
||||
if (e0 >= 0 && e1 >= 0 && e2 >= 0) {
|
||||
var zPix = z + zPush;
|
||||
if (zPix > zMaxTri) zPix = zMaxTri;
|
||||
if (zPix > 1.0) zPix = 1.0;
|
||||
if (zPix < buf[idx]) { buf[idx] = zPix; }
|
||||
}
|
||||
e0 += e0a; e1 += e1a; e2 += e2a;
|
||||
z += dzdx;
|
||||
idx++;
|
||||
}
|
||||
e0row += e0b; e1row += e1b; e2row += e2b;
|
||||
zrow += dzdy;
|
||||
}
|
||||
}
|
||||
|
||||
public function isOccluded(
|
||||
minX: Float, minY: Float, minZ: Float,
|
||||
maxX: Float, maxY: Float, maxZ: Float,
|
||||
vp: iron.math.Mat4,
|
||||
screenW: Int, screenH: Int
|
||||
): Bool {
|
||||
return hiz.isOccluded(minX, minY, minZ, maxX, maxY, maxZ, vp, screenW, screenH);
|
||||
}
|
||||
}
|
||||