This commit is contained in:
zack 2025-05-23 21:13:53 -04:00
commit 444f800536
No known key found for this signature in database
GPG key ID: EE8A2B709E2401D1
122 changed files with 17137 additions and 0 deletions

9
data/CMakeLists.txt Normal file
View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.19)
project(SharedUtils CXX C)
include(../CMake/CommonMacros.txt)
file(GLOB_RECURSE SHADER_FILES LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} shaders/gltf/*.vert shaders/gltf/*.frag shaders/gltf/*.geom shaders/gltf/*.sp src/*.comp)
add_library(Shaders INTERFACE ${SHADER_FILES})

BIN
data/OpenSans-Light.ttf Normal file

Binary file not shown.

BIN
data/brdfLUT.ktx Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,11 @@
Model Information:
* title: Medieval Fantasy Book
* source: https://sketchfab.com/3d-models/medieval-fantasy-book-06d5a80a04fc4c5ab552759e9a97d91a
* author: Pixel (https://sketchfab.com/stefan.lengyel1)
Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.
If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Medieval Fantasy Book" (https://sketchfab.com/3d-models/medieval-fantasy-book-06d5a80a04fc4c5ab552759e9a97d91a) by Pixel (https://sketchfab.com/stefan.lengyel1) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)

Binary file not shown.

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

Binary file not shown.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,4 @@
https://sketchfab.com/3d-models/orrery-44849150982d4c228fca951a66df604f
Eranekao

Binary file not shown.

After

Width:  |  Height:  |  Size: 768 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 833 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 696 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

BIN
data/piazza_bologni_1k.hdr Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
data/rot_texture.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

BIN
data/rubber_duck/scene.bin Normal file

Binary file not shown.

231
data/rubber_duck/scene.gltf Normal file
View file

@ -0,0 +1,231 @@
{
"accessors": [
{
"bufferView": 2,
"componentType": 5126,
"count": 5676,
"max": [
0.36252099275588989,
0.51303797960281372,
1.0525829792022705
],
"min": [
-0.36252099275588989,
-0.48232200741767883,
0.00014600000577047467
],
"type": "VEC3"
},
{
"bufferView": 2,
"byteOffset": 68112,
"componentType": 5126,
"count": 5676,
"max": [
0.99994742870330811,
0.99997341632843018,
0.9999920129776001
],
"min": [
-0.99994742870330811,
-0.99984163045883179,
-1
],
"type": "VEC3"
},
{
"bufferView": 3,
"componentType": 5126,
"count": 5676,
"max": [
0.99985033273696899,
0.99970757961273193,
0.99825453758239746,
1
],
"min": [
-1,
-0.99970918893814087,
-0.99825495481491089,
-1
],
"type": "VEC4"
},
{
"bufferView": 1,
"componentType": 5126,
"count": 5676,
"max": [
0.9983140230178833,
0.87805598974227905
],
"min": [
0.00010900000052060932,
0.00010900000052060932
],
"type": "VEC2"
},
{
"bufferView": 0,
"componentType": 5125,
"count": 33216,
"max": [
5675
],
"min": [
0
],
"type": "SCALAR"
}
],
"asset": {
"extras": {
"author": "emilsvfx (https://sketchfab.com/emilsvfx)",
"license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
"source": "https://sketchfab.com/models/a84cecb600c04eeba60d02f99b8b154b",
"title": "Rubber Duck"
},
"generator": "Sketchfab-3.37.2",
"version": "2.0"
},
"bufferViews": [
{
"buffer": 0,
"byteLength": 132864,
"byteOffset": 0,
"name": "floatBufferViews",
"target": 34963
},
{
"buffer": 0,
"byteLength": 45408,
"byteOffset": 132864,
"byteStride": 8,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 136224,
"byteOffset": 178272,
"byteStride": 12,
"name": "floatBufferViews",
"target": 34962
},
{
"buffer": 0,
"byteLength": 90816,
"byteOffset": 314496,
"byteStride": 16,
"name": "floatBufferViews",
"target": 34962
}
],
"buffers": [
{
"byteLength": 405312,
"uri": "scene.bin"
}
],
"images": [
{
"uri": "textures/Duck_baseColor.png"
}
],
"materials": [
{
"doubleSided": true,
"emissiveFactor": [
0,
0,
0
],
"name": "Duck",
"pbrMetallicRoughness": {
"baseColorFactor": [
1,
1,
1,
1
],
"baseColorTexture": {
"index": 0,
"texCoord": 0
},
"metallicFactor": 0,
"roughnessFactor": 0.84349154199999998
}
}
],
"meshes": [
{
"primitives": [
{
"attributes": {
"NORMAL": 1,
"POSITION": 0,
"TANGENT": 2,
"TEXCOORD_0": 3
},
"indices": 4,
"material": 0,
"mode": 4
}
]
}
],
"nodes": [
{
"children": [
1
],
"name": "RootNode (gltf orientation matrix)",
"rotation": [
-0.70710678118654746,
-0,
-0,
0.70710678118654757
]
},
{
"children": [
2
],
"name": "RootNode (model correction matrix)"
},
{
"children": [
3
],
"name": "duck.obj.cleaner.materialmerger.gles"
},
{
"mesh": 0,
"name": ""
}
],
"samplers": [
{
"magFilter": 9729,
"minFilter": 9987,
"wrapS": 10497,
"wrapT": 10497
}
],
"scene": 0,
"scenes": [
{
"name": "OSG_Scene",
"nodes": [
0
]
}
],
"textures": [
{
"sampler": 0,
"source": 0
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

20
data/shaders/AlphaTest.sp Normal file
View file

@ -0,0 +1,20 @@
//
void runAlphaTest(float alpha, float alphaThreshold)
{
if (alphaThreshold > 0.0) {
// http://alex-charlton.com/posts/Dithering_on_the_GPU/
// https://forums.khronos.org/showthread.php/5091-screen-door-transparency
mat4 thresholdMatrix = mat4(
1.0 / 17.0, 9.0 / 17.0, 3.0 / 17.0, 11.0 / 17.0,
13.0 / 17.0, 5.0 / 17.0, 15.0 / 17.0, 7.0 / 17.0,
4.0 / 17.0, 12.0 / 17.0, 2.0 / 17.0, 10.0 / 17.0,
16.0 / 17.0, 8.0 / 17.0, 14.0 / 17.0, 6.0 / 17.0
);
alpha = clamp(alpha - 0.5 * thresholdMatrix[int(mod(gl_FragCoord.x, 4.0))][int(mod(gl_FragCoord.y, 4.0))], 0.0, 1.0);
if (alpha < alphaThreshold)
discard;
}
}

76
data/shaders/Blur.comp Normal file
View file

@ -0,0 +1,76 @@
//
layout (local_size_x = 16, local_size_y = 16) in;
layout (set = 0, binding = 0) uniform texture2D kTextures2D[];
layout (set = 0, binding = 1) uniform sampler kSamplers[];
layout (set = 0, binding = 2, rgba8) uniform writeonly image2D kTextures2DOut[];
ivec2 textureBindlessSize2D(uint textureid) {
return textureSize(nonuniformEXT(kTextures2D[textureid]), 0);
}
vec4 textureBindless2D(uint textureid, vec2 uv) {
return textureLod(nonuniformEXT(sampler2D(kTextures2D[textureid], kSamplers[0])), uv, 0);
}
layout (constant_id = 0) const bool kIsHorizontal = true;
layout(push_constant) uniform PushConstants {
uint texDepth;
uint texIn;
uint texOut;
float depthThreshold;
} pc;
const int kFilterSize = 17;
// https://drdesten.github.io/web/tools/gaussian_kernel/
const float gaussWeights[kFilterSize] = float[](
0.00001525878906,
0.0002441406250,
0.001831054688,
0.008544921875,
0.02777099609,
0.06665039063,
0.1221923828,
0.1745605469,
0.1963806152,
0.1745605469,
0.1221923828,
0.06665039063,
0.02777099609,
0.008544921875,
0.001831054688,
0.0002441406250,
0.00001525878906
);
void main() {
const vec2 size = textureBindlessSize2D(pc.texIn).xy;
const vec2 xy = gl_GlobalInvocationID.xy;
if (xy.x > size.x || xy.y > size.y)
return;
const vec2 texCoord = (gl_GlobalInvocationID.xy + vec2(0.5)) / size;
const float texScaler = 1.0 / (kIsHorizontal ? size.x : size.y);
vec3 c = vec3(0.0);
vec3 fragColor = textureBindless2D(pc.texIn, texCoord).rgb;
float fragDepth = textureBindless2D(pc.texDepth, texCoord).r;
for ( int i = 0; i != kFilterSize; i++ ) {
float offset = float(i - kFilterSize/2);
vec2 uv = texCoord + texScaler * (kIsHorizontal ? vec2(offset, 0) : vec2(0, offset));
vec3 color = textureBindless2D(pc.texIn, uv).rgb;
float depth = textureBindless2D(pc.texDepth, uv).r;
// bilateral blur
float weight = clamp(abs(depth - fragDepth) * pc.depthThreshold, 0.0, 1.0);
c += mix(color, fragColor, weight) * gaussWeights[i];
}
imageStore(kTextures2DOut[pc.texOut], ivec2(xy), vec4(c, 1.0) );
}

13
data/shaders/Grid.frag Normal file
View file

@ -0,0 +1,13 @@
//
#version 460 core
#include <data/shaders/GridParameters.h>
#include <data/shaders/GridCalculation.h>
layout (location=0) in vec2 uv;
layout (location=1) in vec2 camPos;
layout (location=0) out vec4 out_FragColor;
void main() {
out_FragColor = gridColor(uv, camPos);
}

39
data/shaders/Grid.vert Normal file
View file

@ -0,0 +1,39 @@
//
#version 460 core
#include <data/shaders/GridParameters.h>
layout(push_constant) uniform PerFrameData {
mat4 MVP;
vec4 cameraPos;
vec4 origin;
};
layout (location=0) out vec2 uv;
layout (location=1) out vec2 out_camPos;
const vec3 pos[4] = vec3[4](
vec3(-1.0, 0.0, -1.0),
vec3( 1.0, 0.0, -1.0),
vec3( 1.0, 0.0, 1.0),
vec3(-1.0, 0.0, 1.0)
);
const int indices[6] = int[6](
0, 1, 2, 2, 3, 0
);
void main() {
int idx = indices[gl_VertexIndex];
vec3 position = pos[idx] * gridSize;
position.x += cameraPos.x;
position.z += cameraPos.z;
position += origin.xyz;
out_camPos = cameraPos.xz;
gl_Position = MVP * vec4(position, 1.0);
uv = position.xz;
}

View file

@ -0,0 +1,54 @@
float log10(float x) {
return log(x) / log(10.0);
}
float satf(float x) {
return clamp(x, 0.0, 1.0);
}
vec2 satv(vec2 x) {
return clamp(x, vec2(0.0), vec2(1.0));
}
float max2(vec2 v) {
return max(v.x, v.y);
}
vec4 gridColor(vec2 uv, vec2 camPos) {
vec2 dudv = vec2(
length(vec2(dFdx(uv.x), dFdy(uv.x))),
length(vec2(dFdx(uv.y), dFdy(uv.y)))
);
float lodLevel = max(0.0, log10((length(dudv) * gridMinPixelsBetweenCells) / gridCellSize) + 1.0);
float lodFade = fract(lodLevel);
// cell sizes for lod0, lod1 and lod2
float lod0 = gridCellSize * pow(10.0, floor(lodLevel));
float lod1 = lod0 * 10.0;
float lod2 = lod1 * 10.0;
// each anti-aliased line covers up to 4 pixels
dudv *= 4.0;
// set grid coordinates to the centers of anti-aliased lines for subsequent alpha calculations
uv += dudv * 0.5;
// calculate absolute distances to cell line centers for each lod and pick max X/Y to get coverage alpha value
float lod0a = max2( vec2(1.0) - abs(satv(mod(uv, lod0) / dudv) * 2.0 - vec2(1.0)) );
float lod1a = max2( vec2(1.0) - abs(satv(mod(uv, lod1) / dudv) * 2.0 - vec2(1.0)) );
float lod2a = max2( vec2(1.0) - abs(satv(mod(uv, lod2) / dudv) * 2.0 - vec2(1.0)) );
uv -= camPos;
// blend between falloff colors to handle LOD transition
vec4 c = lod2a > 0.0 ? gridColorThick : lod1a > 0.0 ? mix(gridColorThick, gridColorThin, lodFade) : gridColorThin;
// calculate opacity falloff based on distance to grid extents
float opacityFalloff = (1.0 - satf(length(uv) / gridSize));
// blend between LOD level alphas and scale with opacity falloff
c.a *= (lod2a > 0.0 ? lod2a : lod1a > 0.0 ? lod1a : (lod0a * (1.0-lodFade))) * opacityFalloff;
return c;
}

View file

@ -0,0 +1,14 @@
// extents of grid in world coordinates
float gridSize = 100.0;
// size of one cell
float gridCellSize = 0.025;
// color of thin lines
vec4 gridColorThin = vec4(0.5, 0.5, 0.5, 1.0);
// color of thick lines (every tenth line)
vec4 gridColorThick = vec4(0.0, 0.0, 0.0, 1.0);
// minimum number of pixels between cell lines before LOD switch should occur.
const float gridMinPixelsBetweenCells = 2.0;

12
data/shaders/Quad.frag Normal file
View file

@ -0,0 +1,12 @@
//
layout (location=0) in vec2 uv;
layout (location=0) out vec4 out_FragColor;
layout(push_constant) uniform PerFrameData {
uint textureId;
};
void main() {
out_FragColor = textureBindless2D(textureId, 0, uv);
};

11
data/shaders/Quad.vert Normal file
View file

@ -0,0 +1,11 @@
//
#version 460
layout (location=0) out vec2 uv;
// https://www.saschawillems.de/blog/2016/08/13/vulkan-tutorial-on-rendering-a-fullscreen-quad-without-buffers/
void main() {
uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(uv * 2.0 + -1.0, 0.0, 1.0);
}

View file

@ -0,0 +1,11 @@
//
#version 460
layout (location=0) out vec2 uv;
// https://www.saschawillems.de/blog/2016/08/13/vulkan-tutorial-on-rendering-a-fullscreen-quad-without-buffers/
void main() {
uv = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(uv * vec2(2, -2) + vec2(-1, 1), 0.0, 1.0);
}

19
data/shaders/Shadow.sp Normal file
View file

@ -0,0 +1,19 @@
//
float PCF3x3(vec3 uvw, uint textureid, uint samplerid) {
float size = 1.0 / textureBindlessSize2D(textureid).x; // assume square texture
float shadow = 0.0;
for (int v=-1; v<=+1; v++)
for (int u=-1; u<=+1; u++)
shadow += textureBindless2DShadow(textureid, samplerid, uvw + size * vec3(u, v, 0));
return shadow / 9;
}
float shadow(vec4 s, uint textureid, uint samplerid) {
s = s / s.w;
if (s.z > -1.0 && s.z < 1.0) {
float shadowSample = PCF3x3(vec3(s.x, 1.0 - s.y, s.z), textureid, samplerid);
return mix(0.3, 1.0, shadowSample);
}
return 1.0;
}

42
data/shaders/UtilsPBR.sp Normal file
View file

@ -0,0 +1,42 @@
//
const float M_PI = 3.141592653589793;
vec4 SRGBtoLINEAR(vec4 srgbIn) {
vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
return vec4(linOut, srgbIn.a);
}
// http://www.thetenthplanet.de/archives/1180
// modified to fix handedness of the resulting cotangent frame
mat3 cotangentFrame( vec3 N, vec3 p, vec2 uv ) {
// get edge vectors of the pixel triangle
vec3 dp1 = dFdx( p );
vec3 dp2 = dFdy( p );
vec2 duv1 = dFdx( uv );
vec2 duv2 = dFdy( uv );
// solve the linear system
vec3 dp2perp = cross( dp2, N );
vec3 dp1perp = cross( N, dp1 );
vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
// construct a scale-invariant frame
float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );
// calculate handedness of the resulting cotangent frame
float w = (dot(cross(N, T), B) < 0.0) ? -1.0 : 1.0;
// adjust tangent if needed
T = T * w;
return mat3( T * invmax, B * invmax, N );
}
vec3 perturbNormal(vec3 n, vec3 v, vec3 normalSample, vec2 uv) {
vec3 map = normalize( 2.0 * normalSample - vec3(1.0) );
mat3 TBN = cotangentFrame(n, v, uv);
return normalize(TBN * map);
}

635
data/shaders/gltf/PBR.sp Normal file
View file

@ -0,0 +1,635 @@
// Based on: https://github.com/KhronosGroup/glTF-WebGL-PBR/blob/master/shaders/pbr-frag.glsl
// Encapsulate the various inputs used by the various functions in the shading equation
// We store values in this struct to simplify the integration of alternative implementations
// of the shading terms, outlined in the Readme.MD Appendix.
struct PBRInfo {
// geometry properties
float NdotL; // cos angle between normal and light direction
float NdotV; // cos angle between normal and view direction
float NdotH; // cos angle between normal and half vector
float LdotH; // cos angle between light direction and half vector
float VdotH; // cos angle between view direction and half vector
// Normal
vec3 n; // Sahding normal
vec3 ng; // Geometry normal
vec3 t; // Geometry tangent
vec3 b; // Geometry bitangent
vec3 v; // vector from surface point to camera
// material properties
float perceptualRoughness; // roughness value, as authored by the model creator (input to shader)
vec3 reflectance0; // full reflectance color (normal incidence angle)
vec3 reflectance90; // reflectance color at grazing angle
float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2])
vec3 diffuseColor; // color contribution from diffuse lighting
vec3 specularColor; // color contribution from specular lighting
vec4 baseColor;
float metallic;
float sheenRoughnessFactor;
vec3 sheenColorFactor;
vec3 clearcoatF0;
vec3 clearcoatF90;
float clearcoatFactor;
vec3 clearcoatNormal;
float clearcoatRoughness;
// KHR_materials_specular
float specularWeight; // product of specularFactor and specularTexture.a
float transmissionFactor;
float thickness;
vec4 attenuation; // rgb - color, w - distance
// KHR_materials_iridescence
float iridescenceFactor;
float iridescenceIor;
float iridescenceThickness;
// KHR_materials_anisotropy
vec3 anisotropicT;
vec3 anisotropicB;
float anisotropyStrength;
float ior;
};
const float M_PI = 3.141592653589793;
vec4 SRGBtoLINEAR(vec4 srgbIn) {
vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
return vec4(linOut, srgbIn.a);
}
float clampedDot(vec3 x, vec3 y) {
return clamp(dot(x, y), 0.0, 1.0);
}
//
// Fresnel
//
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// https://github.com/wdas/brdf/tree/master/src/brdfs
// https://google.github.io/filament/Filament.md.html
//
// The following equation models the Fresnel reflectance term of the spec equation (aka F())
// Implementation of fresnel from [4], Equation 15
vec3 F_Schlick(vec3 f0, vec3 f90, float VdotH) {
return f0 + (f90 - f0) * pow(clamp(1.0 - VdotH, 0.0, 1.0), 5.0);
}
float F_Schlick(float f0, float f90, float VdotH) {
float x = clamp(1.0 - VdotH, 0.0, 1.0);
float x2 = x * x;
float x5 = x * x2 * x2;
return f0 + (f90 - f0) * x5;
}
float F_Schlick(float f0, float VdotH) {
float f90 = 1.0; //clamp(50.0 * f0, 0.0, 1.0);
return F_Schlick(f0, f90, VdotH);
}
vec3 F_Schlick(vec3 f0, float f90, float VdotH) {
float x = clamp(1.0 - VdotH, 0.0, 1.0);
float x2 = x * x;
float x5 = x * x2 * x2;
return f0 + (f90 - f0) * x5;
}
vec3 F_Schlick(vec3 f0, float VdotH) {
float f90 = 1.0; //clamp(dot(f0, vec3(50.0 * 0.33)), 0.0, 1.0);
return F_Schlick(f0, f90, VdotH);
}
vec3 Schlick_to_F0(vec3 f, vec3 f90, float VdotH) {
float x = clamp(1.0 - VdotH, 0.0, 1.0);
float x2 = x * x;
float x5 = clamp(x * x2 * x2, 0.0, 0.9999);
return (f - f90 * x5) / (1.0 - x5);
}
float Schlick_to_F0(float f, float f90, float VdotH) {
float x = clamp(1.0 - VdotH, 0.0, 1.0);
float x2 = x * x;
float x5 = clamp(x * x2 * x2, 0.0, 0.9999);
return (f - f90 * x5) / (1.0 - x5);
}
vec3 Schlick_to_F0(vec3 f, float VdotH) {
return Schlick_to_F0(f, vec3(1.0), VdotH);
}
float Schlick_to_F0(float f, float VdotH) {
return Schlick_to_F0(f, 1.0, VdotH);
}
// Smith Joint GGX
// Note: Vis = G / (4 * NdotL * NdotV)
// see Eric Heitz. 2014. Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs. Journal of Computer Graphics Techniques, 3
// see Real-Time Rendering. Page 331 to 336.
// see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg)
float V_GGX(float NdotL, float NdotV, float alphaRoughness) {
float alphaRoughnessSq = alphaRoughness * alphaRoughness;
float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq);
float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq);
float GGX = GGXV + GGXL;
return GGX > 0.0 ? 0.5 / GGX : 0.0;
}
// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
float D_GGX(float NdotH, float alphaRoughness) {
float alphaRoughnessSq = alphaRoughness * alphaRoughness;
float f = (NdotH * NdotH) * (alphaRoughnessSq - 1.0) + 1.0;
return alphaRoughnessSq / (M_PI * f * f);
}
// specularWeight is introduced with KHR_materials_specular
vec3 getIBLRadianceLambertian(float NdotV, vec3 n, float roughness, vec3 diffuseColor, vec3 F0, float specularWeight, EnvironmentMapDataGPU envMap) {
vec2 brdfSamplePoint = clamp(vec2(NdotV, roughness), vec2(0.0, 0.0), vec2(1.0, 1.0));
vec2 f_ab = sampleBRDF_LUT(brdfSamplePoint, envMap).rg;
vec3 irradiance = sampleEnvMapIrradiance(n.xyz, envMap).rgb;
// see https://bruop.github.io/ibl/#single_scattering_results at Single Scattering Results
// Roughness dependent fresnel, from Fdez-Aguera
vec3 Fr = max(vec3(1.0 - roughness), F0) - F0;
vec3 k_S = F0 + Fr * pow(1.0 - NdotV, 5.0);
vec3 FssEss = specularWeight * k_S * f_ab.x + f_ab.y; // <--- GGX / specular light contribution (scale it down if the specularWeight is low)
// Multiple scattering, from Fdez-Aguera
float Ems = (1.0 - (f_ab.x + f_ab.y));
vec3 F_avg = specularWeight * (F0 + (1.0 - F0) / 21.0);
vec3 FmsEms = Ems * FssEss * F_avg / (1.0 - F_avg * Ems);
vec3 k_D = diffuseColor * (1.0 - FssEss + FmsEms); // we use +FmsEms as indicated by the formula in the blog post (might be a typo in the implementation)
return (FmsEms + k_D) * irradiance;
}
//https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#acknowledgments AppendixB
vec3 getBRDFLambertian(vec3 f0, vec3 f90, vec3 diffuseColor, float specularWeight, float VdotH) {
// see https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
return (1.0 - specularWeight * F_Schlick(f0, f90, VdotH)) * (diffuseColor / M_PI);
}
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#acknowledgments AppendixB
vec3 getBRDFSpecularGGX(vec3 f0, vec3 f90, float alphaRoughness, float specularWeight, float VdotH, float NdotL, float NdotV, float NdotH) {
vec3 F = F_Schlick(f0, f90, VdotH);
float Vis = V_GGX(NdotL, NdotV, alphaRoughness);
float D = D_GGX(NdotH, alphaRoughness);
return specularWeight * F * Vis * D;
}
vec3 getIBLRadianceGGX(vec3 n, vec3 v, float roughness, vec3 F0, float specularWeight, EnvironmentMapDataGPU envMap ) {
float NdotV = clampedDot(n, v);
float mipCount = float(sampleEnvMapQueryLevels(envMap));
float lod = roughness * float(mipCount - 1);
vec3 reflection = normalize(reflect(-v, n));
vec2 brdfSamplePoint = clamp(vec2(NdotV, roughness), vec2(0.0, 0.0), vec2(1.0, 1.0));
vec3 f_ab = sampleBRDF_LUT(brdfSamplePoint, envMap).rgb;
vec3 specularLight = sampleEnvMapLod(reflection.xyz, lod, envMap).rgb;
// see https://bruop.github.io/ibl/#single_scattering_results at Single Scattering Results
// Roughness dependent fresnel, from Fdez-Aguera
vec3 Fr = max(vec3(1.0 - roughness), F0) - F0;
vec3 k_S = F0 + Fr * pow(1.0 - NdotV, 5.0);
vec3 FssEss = k_S * f_ab.x + f_ab.y;
return specularWeight * specularLight * FssEss;
}
// Calculation of the lighting contribution from an optional Image Based Light source.
// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
// See our README.md on Environment Maps [3] for additional discussion.
vec3 getIBLRadianceContributionGGX(PBRInfo pbrInputs, float specularWeight, EnvironmentMapDataGPU envMap) {
vec3 n = pbrInputs.n;
vec3 v = pbrInputs.v;
vec3 reflection = normalize(reflect(-v, n));
float mipCount = float(sampleEnvMapQueryLevels(envMap));
float lod = pbrInputs.perceptualRoughness * (mipCount - 1);
// retrieve a scale and bias to F0. See [1], Figure 3
vec2 brdfSamplePoint = clamp(vec2(pbrInputs.NdotV, pbrInputs.perceptualRoughness), vec2(0.0, 0.0), vec2(1.0, 1.0));
vec3 brdf = sampleBRDF_LUT(brdfSamplePoint, envMap).rgb;
// HDR envmaps are already linear
vec3 specularLight = sampleEnvMapLod(reflection.xyz, lod, envMap).rgb;
// see https://bruop.github.io/ibl/#single_scattering_results at Single Scattering Results
// Roughness dependent fresnel, from Fdez-Aguera
vec3 Fr = max(vec3(1.0 - pbrInputs.perceptualRoughness), pbrInputs.reflectance0) - pbrInputs.reflectance0;
vec3 k_S = pbrInputs.reflectance0 + Fr * pow(1.0 - pbrInputs.NdotV, 5.0);
vec3 FssEss = k_S * brdf.x + brdf.y;
return specularWeight * specularLight * FssEss;
}
// Disney Implementation of diffuse from Physically-Based Shading at Disney by Brent Burley. See Section 5.3.
// http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
vec3 diffuseBurley(PBRInfo pbrInputs) {
float f90 = 2.0 * pbrInputs.LdotH * pbrInputs.LdotH * pbrInputs.alphaRoughness - 0.5;
return (pbrInputs.diffuseColor / M_PI) * (1.0 + f90 * pow((1.0 - pbrInputs.NdotL), 5.0)) * (1.0 + f90 * pow((1.0 - pbrInputs.NdotV), 5.0));
}
// The following equation models the Fresnel reflectance term of the spec equation (aka F())
// Implementation of fresnel from [4], Equation 15
vec3 specularReflection(PBRInfo pbrInputs) {
return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0);
}
// This calculates the specular geometric attenuation (aka G()),
// where rougher material will reflect less light back to the viewer.
// This implementation is based on [1] Equation 4, and we adopt their modifications to
// alphaRoughness as input as originally proposed in [2].
float geometricOcclusion(PBRInfo pbrInputs) {
float NdotL = pbrInputs.NdotL;
float NdotV = pbrInputs.NdotV;
float rSqr = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
float attenuationL = 2.0 * NdotL / (NdotL + sqrt(rSqr + (1.0 - rSqr) * (NdotL * NdotL)));
float attenuationV = 2.0 * NdotV / (NdotV + sqrt(rSqr + (1.0 - rSqr) * (NdotV * NdotV)));
return attenuationL * attenuationV;
}
// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
float microfacetDistribution(PBRInfo pbrInputs) {
float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness;
float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0;
return roughnessSq / (M_PI * f * f);
}
vec3 getIBLRadianceCharlie(PBRInfo pbrInputs, EnvironmentMapDataGPU envMap) {
float sheenRoughness = pbrInputs.sheenRoughnessFactor;
vec3 sheenColor = pbrInputs.sheenColorFactor;
float mipCount = float(sampleEnvMapQueryLevels(envMap));
float lod = sheenRoughness * float(mipCount - 1);
vec3 reflection = normalize(reflect(-pbrInputs.v, pbrInputs.n));
vec2 brdfSamplePoint = clamp(vec2(pbrInputs.NdotV, sheenRoughness), vec2(0.0, 0.0), vec2(1.0, 1.0));
float brdf = sampleBRDF_LUT(brdfSamplePoint, envMap).b;
vec3 sheenSample = sampleCharlieEnvMapLod(reflection.xyz, lod, envMap).rgb;
return sheenSample * sheenColor * brdf;
}
PBRInfo calculatePBRInputsMetallicRoughness(InputAttributes tc, vec4 albedo, vec4 mrSample, MetallicRoughnessDataGPU mat) {
PBRInfo pbrInputs;
bool isSpecularGlossiness = (getMaterialType(mat) & 2) != 0;
bool isSpecular = (getMaterialType(mat) & 0x10) != 0;
pbrInputs.ior = getIOR(mat);
vec3 f0 = isSpecularGlossiness ? getSpecularFactor(mat) * mrSample.rgb : vec3(pow((pbrInputs.ior - 1)/(pbrInputs.ior + 1), 2));
float metallic = getMetallicFactor(mat);
metallic = mrSample.b * metallic;
metallic = clamp(metallic, 0.0, 1.0);
pbrInputs.baseColor = albedo;
pbrInputs.metallic = metallic;
pbrInputs.specularWeight = 1.0;
if (isSpecular) {
vec3 dielectricSpecularF0 = min(f0 * getSpecularColorFactor(tc, mat), vec3(1.0));
f0 = mix(dielectricSpecularF0, pbrInputs.baseColor.rgb, metallic);
pbrInputs.specularWeight = getSpecularFactor(tc, mat);//u_KHR_materials_specular_specularFactor * specularTexture.a;
//pbrInputs.diffuseColor = mix(pbrInputs.baseColor.rgb, vec3(0), metallic);
}
float perceptualRoughness = isSpecularGlossiness ? getGlossinessFactor(mat): getRoughnessFactor(mat);
const float c_MinRoughness = 0.04;
// Metallic roughness:
// Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
// This layout intentionally reserves the 'r' channel for (optional) occlusion map data
// Specular Glossiness:
// Glossiness is stored in alpha channel
perceptualRoughness = isSpecularGlossiness ? 1.0 - mrSample.a * perceptualRoughness : clamp(mrSample.g * perceptualRoughness, c_MinRoughness, 1.0);
// Roughness is authored as perceptual roughness; as is convention,
// convert to material roughness by squaring the perceptual roughness [2].
float alphaRoughness = perceptualRoughness * perceptualRoughness;
vec3 diffuseColor = isSpecularGlossiness ? pbrInputs.baseColor.rgb * (1.0 - max(max(f0.r, f0.g), f0.b)) : mix(pbrInputs.baseColor.rgb, vec3(0), metallic);
vec3 specularColor = isSpecularGlossiness ? f0 : mix(f0, pbrInputs.baseColor.rgb, metallic);
// Compute reflectance.
float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
// For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect.
// For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%.
float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0);
vec3 specularEnvironmentR0 = specularColor.rgb;
vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
pbrInputs.alphaRoughness = alphaRoughness;
pbrInputs.perceptualRoughness = perceptualRoughness;
pbrInputs.reflectance0 = specularEnvironmentR0;
pbrInputs.reflectance90 = specularEnvironmentR90;
pbrInputs.diffuseColor = diffuseColor;
pbrInputs.specularColor = specularColor;
return pbrInputs;
}
vec3 calculatePBRLightContribution( inout PBRInfo pbrInputs, vec3 lightDirection, vec3 lightColor ) {
vec3 n = pbrInputs.n;
vec3 v = pbrInputs.v;
vec3 l = normalize(lightDirection); // Vector from surface point to light
vec3 h = normalize(l+v); // Half vector between both l and v
float NdotV = pbrInputs.NdotV;
float NdotL = clamp(dot(n, l), 0.001, 1.0);
float NdotH = clamp(dot(n, h), 0.0, 1.0);
float LdotH = clamp(dot(l, h), 0.0, 1.0);
float VdotH = clamp(dot(v, h), 0.0, 1.0);
vec3 color = vec3(0);
if (NdotL > 0.0 || NdotV > 0.0) {
pbrInputs.NdotL = NdotL;
pbrInputs.NdotH = NdotH;
pbrInputs.LdotH = LdotH;
pbrInputs.VdotH = VdotH;
// Calculate the shading terms for the microfacet specular shading model
vec3 F = specularReflection(pbrInputs);
float G = geometricOcclusion(pbrInputs);
float D = microfacetDistribution(pbrInputs);
// Calculation of analytical lighting contribution
vec3 diffuseContrib = (1.0 - F) * diffuseBurley(pbrInputs);
vec3 specContrib = F * G * D / (4.0 * NdotL * NdotV);
// Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
color = NdotL * lightColor * (diffuseContrib + specContrib);
}
return color;
}
// http://www.thetenthplanet.de/archives/1180
// modified to fix handedness of the resulting cotangent frame
mat3 cotangentFrame( vec3 N, vec3 p, vec2 uv, inout PBRInfo pbrInputs ) {
// get edge vectors of the pixel triangle
vec3 dp1 = dFdx( p );
vec3 dp2 = dFdy( p );
vec2 duv1 = dFdx( uv );
vec2 duv2 = dFdy( uv );
if (length(duv1) <= 1e-2) {
duv1 = vec2(1.0, 0.0);
}
if (length(duv2) <= 1e-2) {
duv2 = vec2(0.0, 1.0);
}
// solve the linear system
vec3 dp2perp = cross( dp2, N );
vec3 dp1perp = cross( N, dp1 );
vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
// construct a scale-invariant frame
float invmax = inversesqrt( max( dot(T,T), dot(B,B) ) );
// calculate handedness of the resulting cotangent frame
float w = dot(cross(N, T), B) < 0.0 ? -1.0 : 1.0;
// adjust tangent if needed
T = T * w;
if (gl_FrontFacing == false) {
N *= -1.0f;
T *= -1.0f;
B *= -1.0f;
}
pbrInputs.t = T * invmax;
pbrInputs.b = B * invmax;
pbrInputs.ng = N;
return mat3( pbrInputs.t, pbrInputs.b, N );
}
void perturbNormal(vec3 n, vec3 v, vec3 normalSample, vec2 uv, inout PBRInfo pbrInputs) {
vec3 map = normalize( 2.0 * normalSample - vec3(1.0) );
mat3 TBN = cotangentFrame(n, v, uv, pbrInputs);
pbrInputs.n = normalize(TBN * map);
}
// Check normals - model has tangents and a normal map!
vec3 getVolumeTransmissionRay(vec3 n, vec3 v, float thickness, float ior, mat4 modelMatrix) {
// direction of refracted light
vec3 refractionVector = refract(-v, n, 1.0 / ior);
// compute rotation-independent scaling of the model matrix
vec3 modelScale = vec3(length(modelMatrix[0].xyz),
length(modelMatrix[1].xyz),
length(modelMatrix[2].xyz));
// the thickness is specified in local space
return normalize(refractionVector) * thickness * modelScale.xyz;
}
vec3 applyVolumeAttenuation(vec3 radiance, float transmissionDistance, vec3 attenuationColor, float attenuationDistance) {
if (attenuationDistance == 0.0) {
// Attenuation distance is + (which we indicate by zero), i.e. the transmitted color is not attenuated at all.
return radiance;
}
// Compute light attenuation using Beer's law.
vec3 attenuationCoefficient = -log(attenuationColor) / attenuationDistance;
vec3 transmittance = exp(-attenuationCoefficient * transmissionDistance); // Beer's law
return transmittance * radiance;
}
float applyIorToRoughness(float roughness, float ior) {
// Scale roughness with IOR so that an IOR of 1.0 results in no microfacet refraction and
// an IOR of 1.5 results in the default amount of microfacet refraction.
return roughness * clamp(ior * 2.0 - 2.0, 0.0, 1.0);
}
vec3 getTransmissionSample(vec2 fragCoord, float roughness, float ior) {
const ivec2 size = textureBindlessSize2D(perFrame.transmissionFramebuffer);
const vec2 uv = fragCoord;
float framebufferLod = log2(float(size.x)) * applyIorToRoughness(roughness, ior);
vec3 transmittedLight = textureBindless2DLod(perFrame.transmissionFramebuffer, perFrame.transmissionFramebufferSampler, uv, framebufferLod).rgb;
// DEBUG:
// return vec3(uv, 0.0);
return transmittedLight;
}
vec3 getIBLVolumeRefraction(vec3 n, vec3 v, float perceptualRoughness, vec3 baseColor, vec3 f0, vec3 f90,
vec3 position, mat4 modelMatrix, mat4 viewProjMatrix, float ior, float thickness, vec3 attenuationColor, float attenuationDistance)
{
vec3 transmissionRay = getVolumeTransmissionRay(n, v, thickness, ior, modelMatrix);
vec3 refractedRayExit = position + transmissionRay;
// Project refracted vector on the framebuffer, while mapping to normalized device coordinates.
vec4 ndcPos = viewProjMatrix * vec4(refractedRayExit, 1.0);
vec2 refractionCoords = ndcPos.xy / ndcPos.w;
refractionCoords += 1.0;
refractionCoords /= 2.0;
refractionCoords.y = 1.0- refractionCoords.y;
// Sample framebuffer to get pixel the refracted ray hits.
vec3 transmittedLight = getTransmissionSample(refractionCoords, perceptualRoughness, ior);
vec3 attenuatedColor = applyVolumeAttenuation(transmittedLight, length(transmissionRay), attenuationColor, attenuationDistance);
// Sample GGX LUT to get the specular component.
float NdotV = clampedDot(n, v);
vec2 brdfSamplePoint = clamp(vec2(NdotV, perceptualRoughness), vec2(0.0, 0.0), vec2(1.0, 1.0));
vec2 brdf = sampleBRDF_LUT(brdfSamplePoint, getEnvironmentMap(getEnvironmentId())).rg;
vec3 specularColor = f0 * brdf.x + f90 * brdf.y;
return (1.0 - specularColor) * attenuatedColor * baseColor;
}
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#range-property
float getRangeAttenuation(float range, float distance) {
if (range <= 0.0) {
// negative range means unlimited
return 1.0 / pow(distance, 2.0);
}
return max(min(1.0 - pow(distance / range, 4.0), 1.0), 0.0) / pow(distance, 2.0);
}
// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#inner-and-outer-cone-angles
float getSpotAttenuation(vec3 pointToLight, vec3 spotDirection, float outerConeCos, float innerConeCos) {
float actualCos = dot(normalize(spotDirection), normalize(-pointToLight));
if (actualCos > outerConeCos) {
if (actualCos < innerConeCos) {
return smoothstep(outerConeCos, innerConeCos, actualCos);
}
return 1.0;
}
return 0.0;
}
vec3 getLightIntensity(Light light, vec3 pointToLight) {
float rangeAttenuation = 1.0;
float spotAttenuation = 1.0;
if (light.type != LightType_Directional) {
rangeAttenuation = getRangeAttenuation(light.range, length(pointToLight));
}
if (light.type == LightType_Spot) {
spotAttenuation = getSpotAttenuation(pointToLight, light.direction, light.outerConeCos, light.innerConeCos);
}
return rangeAttenuation * spotAttenuation * light.intensity * light.color;
}
// Estevez and Kulla http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
float D_Charlie(float sheenRoughness, float NdotH) {
sheenRoughness = max(sheenRoughness, 0.000001); //clamp (0,1]
float alphaG = sheenRoughness * sheenRoughness;
float invR = 1.0 / alphaG;
float cos2h = NdotH * NdotH;
float sin2h = 1.0 - cos2h;
return (2.0 + invR) * pow(sin2h, invR * 0.5) / (2.0 * M_PI);
}
float lambdaSheenNumericHelper(float x, float alphaG) {
float oneMinusAlphaSq = (1.0 - alphaG) * (1.0 - alphaG);
float a = mix(21.5473, 25.3245, oneMinusAlphaSq);
float b = mix(3.82987, 3.32435, oneMinusAlphaSq);
float c = mix(0.19823, 0.16801, oneMinusAlphaSq);
float d = mix(-1.97760, -1.27393, oneMinusAlphaSq);
float e = mix(-4.32054, -4.85967, oneMinusAlphaSq);
return a / (1.0 + b * pow(x, c)) + d * x + e;
}
float lambdaSheen(float cosTheta, float alphaG) {
if (abs(cosTheta) < 0.5) {
return exp(lambdaSheenNumericHelper(cosTheta, alphaG));
}
return exp(2.0 * lambdaSheenNumericHelper(0.5, alphaG) - lambdaSheenNumericHelper(1.0 - cosTheta, alphaG));
}
float V_Sheen(float NdotL, float NdotV, float sheenRoughness) {
sheenRoughness = max(sheenRoughness, 0.000001); //clamp (0,1]
float alphaG = sheenRoughness * sheenRoughness;
return clamp(1.0 / ((1.0 + lambdaSheen(NdotV, alphaG) + lambdaSheen(NdotL, alphaG)) * (4.0 * NdotV * NdotL)), 0.0, 1.0);
}
// f_sheen
vec3 getBRDFSpecularSheen(vec3 sheenColor, float sheenRoughness, float NdotL, float NdotV, float NdotH) {
float sheenDistribution = D_Charlie(sheenRoughness, NdotH);
float sheenVisibility = V_Sheen(NdotL, NdotV, sheenRoughness);
return sheenColor * sheenDistribution * sheenVisibility;
}
vec3 getPunctualRadianceSheen(vec3 sheenColor, float sheenRoughness, float NdotL, float NdotV, float NdotH) {
return NdotL * getBRDFSpecularSheen(sheenColor, sheenRoughness, NdotL, NdotV, NdotH);
}
// https://github.com/DassaultSystemes-Technology/dspbr-pt/blob/e7cfa6e9aab2b99065a90694e1f58564d675c1a4/packages/lib/shader/bsdfs/sheen.glsl#L16C1-L20C2
float albedoSheenScalingFactor(float NdotV, float sheenRoughnessFactor) {
float c = 1.0 - NdotV;
float c3 = c * c * c;
return 0.65584461 * c3 + 1.0 / (4.16526551 + exp(-7.97291361 * sqrt(sheenRoughnessFactor) + 6.33516894));
}
float max3(vec3 v) {
return max(max(v.x, v.y), v.z);
}
vec3 getPunctualRadianceClearCoat(vec3 clearcoatNormal, vec3 v, vec3 l, vec3 h, float VdotH, vec3 f0, vec3 f90, float clearcoatRoughness) {
float NdotL = clampedDot(clearcoatNormal, l);
float NdotV = clampedDot(clearcoatNormal, v);
float NdotH = clampedDot(clearcoatNormal, h);
return NdotL * getBRDFSpecularGGX(f0, f90, clearcoatRoughness * clearcoatRoughness, 1.0, VdotH, NdotL, NdotV, NdotH);
}
vec3 getPunctualRadianceTransmission(vec3 normal, vec3 view, vec3 pointToLight, float alphaRoughness, vec3 f0, vec3 f90, vec3 baseColor, float ior) {
float transmissionRougness = applyIorToRoughness(alphaRoughness, ior);
vec3 n = normalize(normal); // Outward direction of surface point
vec3 v = normalize(view); // Direction from surface point to view
vec3 l = normalize(pointToLight);
vec3 l_mirror = normalize(l + 2.0*n*dot(-l, n)); // Mirror light reflection vector on surface
vec3 h = normalize(l_mirror + v); // Halfway vector between transmission light vector and v
float D = D_GGX(clamp(dot(n, h), 0.0, 1.0), transmissionRougness);
vec3 F = F_Schlick(f0, f90, clamp(dot(v, h), 0.0, 1.0));
float Vis = V_GGX(clamp(dot(n, l_mirror), 0.0, 1.0), clamp(dot(n, v), 0.0, 1.0), transmissionRougness);
// Transmission BTDF
return (1.0 - F) * baseColor * D * Vis;
}

View file

@ -0,0 +1,116 @@
//
#version 460
#extension GL_EXT_buffer_reference : require
#extension GL_EXT_scalar_block_layout : require
layout (local_size_x = 16, local_size_y = 1, local_size_z = 1) in;
struct TransformsBuffer {
uint mtxId;
uint matId;
uint nodeRef; // for CPU only
uint meshRef; // for CPU only
uint opaque; // for CPU only
};
struct VertexSkinningData {
vec4 pos;
vec4 norm;
uint bones[8];
float weights[8];
uint meshId;
};
struct VertexData {
vec3 pos;
vec3 norm;
vec4 color;
vec4 uv;
float padding[2];
};
#define MAX_WEIGHTS 8
struct MorphState {
uint meshId;
uint morphTarget[MAX_WEIGHTS];
float weights[MAX_WEIGHTS];
};
layout(std430, buffer_reference) readonly buffer Matrices {
mat4 matrix[];
};
layout(scalar, buffer_reference) readonly buffer MorphStates {
MorphState morphStates[];
};
layout (scalar, buffer_reference) readonly buffer VertexSkinningBuffer {
VertexSkinningData vertices[];
};
layout (scalar, buffer_reference) writeonly buffer VertexBuffer {
VertexData vertices[];
};
layout (scalar, buffer_reference) readonly buffer MorphVertexBuffer {
VertexData vertices[];
};
layout(push_constant) uniform PerFrameData {
Matrices matrices;
MorphStates morphStates;
MorphVertexBuffer morphTargets;
VertexSkinningBuffer inBufferId;
VertexBuffer outBufferId;
uint numMorphStates;
} pc;
void main()
{
// our vertex buffers are always padded to a 16-vertex boundary
uint index = gl_GlobalInvocationID.x;
VertexSkinningData inVtx = pc.inBufferId.vertices[index];
vec4 inPos = vec4(inVtx.pos.xyz, 1.0);
vec4 inNorm = vec4(inVtx.norm.xyz, 0.0);
// morphing
if (inVtx.meshId < pc.numMorphStates) {
MorphState ms = pc.morphStates.morphStates[inVtx.meshId];
if (ms.meshId != ~0) {
for (int m = 0; m != MAX_WEIGHTS; m++) {
if (ms.weights[m] > 0) {
VertexData mVtx = pc.morphTargets.vertices[ms.morphTarget[m] + index];
inPos.xyz += mVtx.pos * ms.weights[m];
inNorm.xyz += mVtx.norm * ms.weights[m];
}
}
}
}
vec4 pos = vec4(0);
vec4 norm = vec4(0);
int i = 0;
// skinning
for (; i != MAX_WEIGHTS; i++) {
if (inVtx.bones[i] == ~0)
break;
mat4 boneMat = pc.matrices.matrix[inVtx.bones[i]];
pos += boneMat * inPos * inVtx.weights[i];
norm += transpose(inverse(boneMat)) * inNorm * inVtx.weights[i];
}
if (i == 0) {
pos.xyz = inPos.xyz;
norm.xyz = inNorm.xyz;
}
pc.outBufferId.vertices[index].pos = pos.xyz;
pc.outBufferId.vertices[index].norm = normalize(norm.xyz);
}

View file

@ -0,0 +1,51 @@
//
// gl_BaseInstance - transformId
layout(std430, buffer_reference) buffer Materials;
layout(std430, buffer_reference) buffer Environments;
layout(std430, buffer_reference) buffer Lights;
layout(std430, buffer_reference) buffer PerDrawData {
mat4 model;
mat4 view;
mat4 proj;
vec4 cameraPos;
};
struct TransformsBuffer {
uint mtxId;
uint matId;
uint nodeRef; // for CPU only
uint meshRef; // for CPU only
uint opaque; // for CPU only
};
layout(std430, buffer_reference) readonly buffer Transforms {
TransformsBuffer transforms[];
};
layout(std430, buffer_reference) readonly buffer Matrices {
mat4 matrix[];
};
layout(push_constant) uniform PerFrameData {
PerDrawData drawable;
Materials materials;
Environments environments;
Lights lights;
Transforms transforms;
Matrices matrices;
uint envId;
uint transmissionFramebuffer;
uint transmissionFramebufferSampler;
uint lightsCount;
} perFrame;
uint getEnvironmentId() {
return perFrame.envId;
}
mat4 getViewProjection() {
return perFrame.drawable.proj * perFrame.drawable.view;
}

View file

@ -0,0 +1,67 @@
//
struct MetallicRoughnessDataGPU {
vec4 baseColorFactor;
vec4 metallicRoughnessNormalOcclusion; // Packed metallicFactor, roughnessFactor, normalScale, occlusionStrength
vec4 specularGlossiness; // Packed specularFactor.xyz, glossiness
vec4 sheenFactors;
vec4 clearcoatTransmissionThickness;
vec4 specularFactors;
vec4 attenuation;
vec4 emissiveFactorAlphaCutoff; // vec3 emissiveFactor + float AlphaCutoff
uint occlusionTexture;
uint occlusionTextureSampler;
uint occlusionTextureUV;
uint emissiveTexture;
uint emissiveTextureSampler;
uint emissiveTextureUV;
uint baseColorTexture;
uint baseColorTextureSampler;
uint baseColorTextureUV;
uint metallicRoughnessTexture;
uint metallicRoughnessTextureSampler;
uint metallicRoughnessTextureUV;
uint normalTexture;
uint normalTextureSampler;
uint normalTextureUV;
uint sheenColorTexture;
uint sheenColorTextureSampler;
uint sheenColorTextureUV;
uint sheenRoughnessTexture;
uint sheenRoughnessTextureSampler;
uint sheenRoughnessTextureUV;
uint clearCoatTexture;
uint clearCoatTextureSampler;
uint clearCoatTextureUV;
uint clearCoatRoughnessTexture;
uint clearCoatRoughnessTextureSampler;
uint clearCoatRoughnessTextureUV;
uint clearCoatNormalTexture;
uint clearCoatNormalTextureSampler;
uint clearCoatNormalTextureUV;
uint specularTexture;
uint specularTextureSampler;
uint specularTextureUV;
uint specularColorTexture;
uint specularColorTextureSampler;
uint specularColorTextureUV;
uint transmissionTexture;
uint transmissionTextureSampler;
uint transmissionTextureUV;
uint thicknessTexture;
uint thicknessTextureSampler;
uint thicknessTextureUV;
uint iridescenceTexture;
uint iridescenceTextureSampler;
uint iridescenceTextureUV;
uint iridescenceThicknessTexture;
uint iridescenceThicknessTextureSampler;
uint iridescenceThicknessTextureUV;
uint anisotropyTexture;
uint anisotropyTextureSampler;
uint anisotropyTextureUV;
uint alphaMode;
uint materialType;
float ior;
uint padding[2];
};

View file

@ -0,0 +1,229 @@
#include <data/shaders/gltf/common.sp>
#include <data/shaders/gltf/common_material.sp>
const int kMaxAttributes = 2;
struct InputAttributes {
vec2 uv[kMaxAttributes];
};
struct Light {
vec3 direction;
float range;
vec3 color;
float intensity;
vec3 position;
float innerConeCos;
float outerConeCos;
int type;
int padding[2];
};
const int LightType_Directional = 0;
const int LightType_Point = 1;
const int LightType_Spot = 2;
struct EnvironmentMapDataGPU {
uint envMapTexture;
uint envMapTextureSampler;
uint envMapTextureIrradiance;
uint envMapTextureIrradianceSampler;
uint texBRDFLUT;
uint texBRDFLUTSampler;
uint envMapTextureCharlie;
uint envMapTextureCharlieSampler;
};
layout(std430, buffer_reference) readonly buffer Materials {
MetallicRoughnessDataGPU material[];
};
layout(std430, buffer_reference) readonly buffer Environments {
EnvironmentMapDataGPU environment[];
};
layout(std430, buffer_reference) readonly buffer Lights {
Light lights[];
};
MetallicRoughnessDataGPU getMaterial(uint idx) {
return perFrame.materials.material[idx];
}
EnvironmentMapDataGPU getEnvironmentMap(uint idx) {
return perFrame.environments.environment[idx];
}
float getMetallicFactor(MetallicRoughnessDataGPU mat) {
return mat.metallicRoughnessNormalOcclusion.x;
}
float getRoughnessFactor(MetallicRoughnessDataGPU mat) {
return mat.metallicRoughnessNormalOcclusion.y;
}
float getNormalScale(MetallicRoughnessDataGPU mat) {
return mat.metallicRoughnessNormalOcclusion.z;
}
float getOcclusionFactor(MetallicRoughnessDataGPU mat) {
return mat.metallicRoughnessNormalOcclusion.w;
}
uint getMaterialType(MetallicRoughnessDataGPU mat) {
return mat.materialType;
}
vec3 getSpecularFactor(MetallicRoughnessDataGPU mat) {
return mat.specularGlossiness.xyz;
}
float getGlossinessFactor(MetallicRoughnessDataGPU mat) {
return mat.specularGlossiness.w;
}
vec4 getEmissiveFactorAlphaCutoff(MetallicRoughnessDataGPU mat) {
return mat.emissiveFactorAlphaCutoff;
}
vec4 getSheenColorFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.sheenColorTexture, mat.sheenColorTextureSampler, tc.uv[mat.sheenColorTextureUV]) * vec4(mat.sheenFactors.xyz, 1.0f);
}
float getSheenRoughnessFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.sheenRoughnessTexture, mat.sheenRoughnessTextureSampler, tc.uv[mat.sheenRoughnessTextureUV]).a * mat.sheenFactors.a;
}
float getClearcoatFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.clearCoatTexture, mat.clearCoatTextureSampler, tc.uv[mat.clearCoatTextureUV]).r * mat.clearcoatTransmissionThickness.x;
}
float getClearcoatRoughnessFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.clearCoatRoughnessTexture, mat.clearCoatRoughnessTextureSampler, tc.uv[mat.clearCoatRoughnessTextureUV]).g * mat.clearcoatTransmissionThickness.y;
}
float getTransmissionFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.transmissionTexture, mat.transmissionTextureSampler, tc.uv[mat.transmissionTextureUV]).r * mat.clearcoatTransmissionThickness.z;
}
float getVolumeTickness(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.thicknessTexture, mat.thicknessTextureSampler, tc.uv[mat.thicknessTextureUV]).g * mat.clearcoatTransmissionThickness.w;
}
vec4 getVolumeAttenuation(MetallicRoughnessDataGPU mat) {
return mat.attenuation;
}
float getIOR(MetallicRoughnessDataGPU mat) {
return mat.ior;
}
vec3 getSpecularColorFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.specularColorTexture, mat.specularColorTextureSampler, tc.uv[mat.specularColorTextureUV]).rgb * mat.specularFactors.rgb;
}
float getSpecularFactor(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.specularTexture, mat.specularTextureSampler, tc.uv[mat.specularTextureUV]).a * mat.specularFactors.a;
}
vec2 getNormalUV(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return mat.normalTextureUV > -1 ? tc.uv[mat.normalTextureUV] : tc.uv[0];
}
vec4 sampleAO(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.occlusionTexture, mat.occlusionTextureSampler, tc.uv[mat.occlusionTextureUV]);
}
vec4 sampleEmissive(InputAttributes tc, MetallicRoughnessDataGPU mat) {
vec2 uv = mat.emissiveTextureUV > -1 ? tc.uv[mat.emissiveTextureUV] : tc.uv[0];
return textureBindless2D(mat.emissiveTexture, mat.emissiveTextureSampler, uv) * vec4(mat.emissiveFactorAlphaCutoff.xyz, 1.0f);
}
vec4 sampleAlbedo(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.baseColorTexture, mat.baseColorTextureSampler, tc.uv[mat.baseColorTextureUV]) * mat.baseColorFactor;
}
vec4 sampleMetallicRoughness(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.metallicRoughnessTexture, mat.metallicRoughnessTextureSampler, tc.uv[mat.metallicRoughnessTextureUV]);
}
vec4 sampleNormal(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.normalTexture, mat.normalTextureSampler, tc.uv[mat.normalTextureUV]);// * mat.metallicRoughnessNormalOcclusion.z;
}
vec4 sampleClearcoatNormal(InputAttributes tc, MetallicRoughnessDataGPU mat) {
return textureBindless2D(mat.clearCoatNormalTexture, mat.clearCoatNormalTextureSampler, tc.uv[mat.clearCoatNormalTextureUV]);
}
vec4 sampleBRDF_LUT(vec2 tc, EnvironmentMapDataGPU map) {
return textureBindless2D(map.texBRDFLUT, map.texBRDFLUTSampler, tc);
}
vec4 sampleEnvMap(vec3 tc, EnvironmentMapDataGPU map) {
return textureBindlessCube(map.envMapTexture, map.envMapTextureSampler, tc);
}
vec4 sampleEnvMapLod(vec3 tc, float lod, EnvironmentMapDataGPU map) {
return textureBindlessCubeLod(map.envMapTexture, map.envMapTextureSampler, tc, lod);
}
vec4 sampleCharlieEnvMapLod(vec3 tc, float lod, EnvironmentMapDataGPU map) {
return textureBindlessCubeLod(map.envMapTextureCharlie, map.envMapTextureCharlieSampler, tc, lod);
}
vec4 sampleEnvMapIrradiance(vec3 tc, EnvironmentMapDataGPU map) {
return textureBindlessCube(map.envMapTextureIrradiance, map.envMapTextureIrradianceSampler, tc);
}
int sampleEnvMapQueryLevels(EnvironmentMapDataGPU map) {
return textureBindlessQueryLevelsCube(map.envMapTexture);
}
vec4 sampleTransmissionFramebuffer(vec2 tc) {
return textureBindless2D(perFrame.transmissionFramebuffer, perFrame.transmissionFramebufferSampler, tc);
}
bool isMaterialTypeSheen(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x4) != 0;
}
bool isMaterialTypeClearCoat(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x8) != 0;
}
bool isMaterialTypeSpecular(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x10) != 0;
}
bool isMaterialTypeTransmission(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x20) != 0;
}
bool isMaterialTypeVolume(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x40) != 0;
}
bool isMaterialTypeUnlit(MetallicRoughnessDataGPU mat) {
return (getMaterialType(mat) & 0x80) != 0;
}
uint getLightsCount() {
return perFrame.lightsCount;
}
Light getLight(uint i) {
return perFrame.lights.lights[i];
}
mat4 getModel() {
uint mtxId = perFrame.transforms.transforms[oBaseInstance].mtxId;
return perFrame.drawable.model * perFrame.matrices.matrix[mtxId];
}
uint getMaterialId() {
return perFrame.transforms.transforms[oBaseInstance].matId;
}

View file

@ -0,0 +1,28 @@
#include <data/shaders/gltf/common.sp>
layout (location = 0) in vec3 pos;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec4 color;
layout (location = 3) in vec2 uv0;
layout (location = 4) in vec2 uv1;
vec3 getPosition() {
return pos;
}
vec3 getNormal() {
return normal;
}
vec4 getColor() {
return color;
}
vec2 getTexCoord(uint i) {
return i == 0 ? uv0 : uv1;
}
mat4 getModel() {
uint mtxId = perFrame.transforms.transforms[gl_BaseInstance].mtxId;
return perFrame.drawable.model * perFrame.matrices.matrix[mtxId];
}

216
data/shaders/gltf/main.frag Normal file
View file

@ -0,0 +1,216 @@
//
layout (location=0) in vec4 uv0uv1;
layout (location=1) in vec3 normal;
layout (location=2) in vec3 worldPos;
layout (location=3) in vec4 color;
layout (location=4) in flat int oBaseInstance;
layout (location=0) out vec4 out_FragColor;
#include <data/shaders/gltf/inputs.frag>
#include <data/shaders/gltf/PBR.sp>
void main()
{
InputAttributes tc;
tc.uv[0] = uv0uv1.xy;
tc.uv[1] = uv0uv1.zw;
MetallicRoughnessDataGPU mat = getMaterial(getMaterialId());
EnvironmentMapDataGPU envMap = getEnvironmentMap(getEnvironmentId());
vec4 Kd = sampleAlbedo(tc, mat) * color;
if ((mat.alphaMode == 1) && (mat.emissiveFactorAlphaCutoff.w > Kd.a)) {
discard;
}
if (isMaterialTypeUnlit(mat)) {
out_FragColor = Kd;
return;
}
vec4 Kao = sampleAO(tc, mat);
vec4 Ke = sampleEmissive(tc, mat);
vec4 mrSample = sampleMetallicRoughness(tc, mat);
bool isSheen = isMaterialTypeSheen(mat);
bool isClearCoat = isMaterialTypeClearCoat(mat);
bool isSpecular = isMaterialTypeSpecular(mat);
bool isTransmission = isMaterialTypeTransmission(mat);
bool isVolume = isMaterialTypeVolume(mat);
vec3 n = normalize(normal);
PBRInfo pbrInputs = calculatePBRInputsMetallicRoughness(tc, Kd, mrSample, mat);
pbrInputs.n = n;
pbrInputs.ng = n;
if (mat.normalTexture != ~0) {
vec3 normalSample = sampleNormal(tc, mat).xyz;
perturbNormal(n, worldPos, normalSample, getNormalUV(tc, mat), pbrInputs);
n = pbrInputs.n;
}
vec3 v = normalize(perFrame.drawable.cameraPos.xyz - worldPos); // Vector from surface point to camera
pbrInputs.v = v;
pbrInputs.NdotV = clamp(abs(dot(pbrInputs.n, pbrInputs.v)), 0.001, 1.0);
if (isSheen) {
pbrInputs.sheenColorFactor = getSheenColorFactor(tc, mat).rgb;
pbrInputs.sheenRoughnessFactor = getSheenRoughnessFactor(tc, mat);
}
vec3 clearCoatContrib = vec3(0);
if (isClearCoat) {
pbrInputs.clearcoatFactor = getClearcoatFactor(tc, mat);
pbrInputs.clearcoatRoughness = clamp(getClearcoatRoughnessFactor(tc, mat), 0.0, 1.0);
pbrInputs.clearcoatF0 = vec3(pow((pbrInputs.ior - 1.0) / (pbrInputs.ior + 1.0), 2.0));
pbrInputs.clearcoatF90 = vec3(1.0);
if (mat.clearCoatNormalTextureUV>-1) {
pbrInputs.clearcoatNormal = mat3(pbrInputs.t, pbrInputs.b, pbrInputs.ng) * sampleClearcoatNormal(tc, mat).rgb;
} else {
pbrInputs.clearcoatNormal =pbrInputs.ng;
}
clearCoatContrib = getIBLRadianceGGX(pbrInputs.clearcoatNormal, pbrInputs.v, pbrInputs.clearcoatRoughness, pbrInputs.clearcoatF0, 1.0, envMap);
}
if (isTransmission) {
pbrInputs.transmissionFactor = getTransmissionFactor(tc, mat);
}
if (isVolume) {
pbrInputs.thickness = getVolumeTickness(tc, mat);
pbrInputs.attenuation = getVolumeAttenuation(mat);
}
// IBL contribution
vec3 specularColor = getIBLRadianceContributionGGX(pbrInputs, pbrInputs.specularWeight, envMap);
vec3 diffuseColor = getIBLRadianceLambertian(pbrInputs.NdotV, n, pbrInputs.perceptualRoughness, pbrInputs.diffuseColor, pbrInputs.reflectance0, pbrInputs.specularWeight, envMap);
vec3 transmission = vec3(0,0,0);
if (isTransmission) {
transmission += getIBLVolumeRefraction(
pbrInputs.n, pbrInputs.v,
pbrInputs.perceptualRoughness,
pbrInputs.diffuseColor, pbrInputs.reflectance0, pbrInputs.reflectance90,
worldPos, getModel(), getViewProjection(),
pbrInputs.ior, pbrInputs.thickness, pbrInputs.attenuation.rgb, pbrInputs.attenuation.w);
}
vec3 sheenColor = vec3(0);
if (isSheen) {
sheenColor += getIBLRadianceCharlie(pbrInputs, envMap);
}
vec3 lights_diffuse = vec3(0);
vec3 lights_specular = vec3(0);
vec3 lights_sheen = vec3(0);
vec3 lights_clearcoat = vec3(0);
vec3 lights_transmission = vec3(0);
float albedoSheenScaling = 1.0;
for (uint i = 0; i < getLightsCount(); ++i)
{
Light light = getLight(i);
vec3 pointToLight = (light.type == LightType_Directional) ? -light.direction : light.position - worldPos;
// BSTF
vec3 l = normalize(pointToLight);
vec3 h = normalize(l + v);
float NdotL = clampedDot(n, l);
float NdotV = clampedDot(n, v);
float NdotH = clampedDot(n, h);
float LdotH = clampedDot(l, h);
float VdotH = clampedDot(v, h);
if (NdotL > 0.0 || NdotV > 0.0)
{
// Calculation of analytical light
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#acknowledgments AppendixB
vec3 intensity = getLightIntensity(light, pointToLight);
lights_diffuse += intensity * NdotL * getBRDFLambertian(pbrInputs.reflectance0, pbrInputs.reflectance90, pbrInputs.diffuseColor, pbrInputs.specularWeight, VdotH);
lights_specular += intensity * NdotL * getBRDFSpecularGGX(pbrInputs.reflectance0, pbrInputs.reflectance90, pbrInputs.alphaRoughness, pbrInputs.specularWeight, VdotH, NdotL, NdotV, NdotH);
if (isSheen) {
lights_sheen += intensity * getPunctualRadianceSheen(pbrInputs.sheenColorFactor, pbrInputs.sheenRoughnessFactor, NdotL, NdotV, NdotH);
albedoSheenScaling = min(1.0 - max3(pbrInputs.sheenColorFactor) * albedoSheenScalingFactor(NdotV, pbrInputs.sheenRoughnessFactor),
1.0 - max3(pbrInputs.sheenColorFactor) * albedoSheenScalingFactor(NdotL, pbrInputs.sheenRoughnessFactor));
}
if (isClearCoat) {
lights_clearcoat += intensity * getPunctualRadianceClearCoat(pbrInputs.clearcoatNormal, v, l, h, VdotH,
pbrInputs.clearcoatF0, pbrInputs.clearcoatF90, pbrInputs.clearcoatRoughness);
}
}
// BDTF
if (isTransmission) {
// If the light ray travels through the geometry, use the point it exits the geometry again.
// That will change the angle to the light source, if the material refracts the light ray.
vec3 transmissionRay = getVolumeTransmissionRay(n, v, pbrInputs.thickness, pbrInputs.ior, getModel());
pointToLight -= transmissionRay;
l = normalize(pointToLight);
vec3 intensity = getLightIntensity(light, pointToLight);
vec3 transmittedLight = intensity * getPunctualRadianceTransmission(n, v, l, pbrInputs.alphaRoughness, pbrInputs.reflectance0, pbrInputs.clearcoatF90, pbrInputs.diffuseColor, pbrInputs.ior);
if (isVolume) {
transmittedLight = applyVolumeAttenuation(transmittedLight, length(transmissionRay), pbrInputs.attenuation.rgb, pbrInputs.attenuation.w);
}
lights_transmission += transmittedLight;
}
}
// ambient occlusion
float occlusion = Kao.r < 0.01 ? 1.0 : Kao.r;
float occlusionStrength = getOcclusionFactor(mat);
diffuseColor = lights_diffuse + mix(diffuseColor, diffuseColor * occlusion, occlusionStrength);
specularColor = lights_specular + mix(specularColor, specularColor * occlusion, occlusionStrength);
sheenColor = lights_sheen + mix(sheenColor, sheenColor * occlusion, occlusionStrength);
vec3 emissiveColor = Ke.rgb * sampleEmissive(tc, mat).rgb;
vec3 clearcoatFresnel = vec3(0);
if (isClearCoat) {
clearcoatFresnel = F_Schlick(pbrInputs.clearcoatF0, pbrInputs.clearcoatF90, clampedDot(pbrInputs.clearcoatNormal, pbrInputs.v));
}
if (isTransmission) {
diffuseColor = mix(diffuseColor, transmission, pbrInputs.transmissionFactor);
}
vec3 color = specularColor + diffuseColor + emissiveColor + sheenColor;
color = color * (1.0 - pbrInputs.clearcoatFactor * clearcoatFresnel) + clearCoatContrib;
color = pow(color, vec3(1.0/2.2));
out_FragColor = vec4(color, 1.0);
// DEBUG
// out_FragColor = vec4((n + vec3(1.0))*0.5, 1.0);
// out_FragColor = vec4((pbrInputs.n + vec3(1.0))*0.5, 1.0);
// out_FragColor = vec4((normal + vec3(1.0))*0.5, 1.0);
// out_FragColor = Kao;
// out_FragColor = Ke;
// out_FragColor = Kd;
// vec2 MeR = mrSample.yz;
// MeR.x *= getMetallicFactor(mat);
// MeR.y *= getRoughnessFactor(mat);
// out_FragColor = vec4(MeR.y,MeR.y,MeR.y, 1.0);
// out_FragColor = vec4(MeR.x,MeR.x,MeR.x, 1.0);
// out_FragColor = mrSample;
// out_FragColor = vec4(transmission, 1.0);
// out_FragColor = vec4(punctualColor, 1.0);
}

View file

@ -0,0 +1,28 @@
//
layout (location=0) out vec4 oUV0UV1;
layout (location=1) out vec3 oNormal;
layout (location=2) out vec3 oWorldPos;
layout (location=3) out vec4 oColor;
layout (location=4) out flat int oBaseInstance;
#include <data/shaders/gltf/inputs.vert>
void main() {
mat4 model = getModel();
mat4 MVP = getViewProjection() * model;
vec3 pos = getPosition();
gl_Position = MVP * vec4(pos, 1.0);
oUV0UV1 = vec4(getTexCoord(0), getTexCoord(1));
oColor = getColor();
mat3 normalMatrix = transpose( inverse(mat3(model)) );
oNormal = normalMatrix * getNormal();
vec4 posClip = model * vec4(pos, 1.0);
oWorldPos = posClip.xyz/posClip.w;
oBaseInstance = gl_BaseInstance;
}

BIN
data/wood.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB