From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- shaders/brdf_integration_map.frag | 93 +++++++++++ shaders/cook_torrance.frag | 255 +++++++++++++++++++++++++++++++ shaders/cook_torrance.vert | 75 +++++++++ shaders/cubemap_filtering.vert | 39 +++++ shaders/debug3d.frag | 21 +++ shaders/debug3d.vert | 15 ++ shaders/immediate_mode.frag | 10 ++ shaders/immediate_mode.vert | 11 ++ shaders/irradiance_map.frag | 65 ++++++++ shaders/prefiltered_environment_map.frag | 78 ++++++++++ shaders/quad.vert | 9 ++ shaders/skyquad.frag | 13 ++ shaders/skyquad.vert | 27 ++++ shaders/view_normal_mapped_normals.frag | 28 ++++ shaders/view_normal_mapped_normals.vert | 22 +++ shaders/view_normals.frag | 11 ++ shaders/view_normals.vert | 15 ++ shaders/view_tangents.frag | 17 +++ shaders/view_tangents.vert | 14 ++ shaders/view_texture.frag | 15 ++ shaders/view_texture.vert | 13 ++ 21 files changed, 846 insertions(+) create mode 100644 shaders/brdf_integration_map.frag create mode 100644 shaders/cook_torrance.frag create mode 100644 shaders/cook_torrance.vert create mode 100644 shaders/cubemap_filtering.vert create mode 100644 shaders/debug3d.frag create mode 100644 shaders/debug3d.vert create mode 100644 shaders/immediate_mode.frag create mode 100644 shaders/immediate_mode.vert create mode 100644 shaders/irradiance_map.frag create mode 100644 shaders/prefiltered_environment_map.frag create mode 100644 shaders/quad.vert create mode 100644 shaders/skyquad.frag create mode 100644 shaders/skyquad.vert create mode 100644 shaders/view_normal_mapped_normals.frag create mode 100644 shaders/view_normal_mapped_normals.vert create mode 100644 shaders/view_normals.frag create mode 100644 shaders/view_normals.vert create mode 100644 shaders/view_tangents.frag create mode 100644 shaders/view_tangents.vert create mode 100644 shaders/view_texture.frag create mode 100644 shaders/view_texture.vert (limited to 'shaders') diff --git a/shaders/brdf_integration_map.frag b/shaders/brdf_integration_map.frag new file mode 100644 index 0000000..bb2cebd --- /dev/null +++ b/shaders/brdf_integration_map.frag @@ -0,0 +1,93 @@ +precision highp float; + +#define PI 3.1415926535897932384626433832795 +#define NUM_SAMPLES 1024 + +in vec2 Texcoord; + +layout (location = 0) out vec2 Color; + +float radical_inverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 hammersley(uint i, uint N) { + return vec2(float(i)/float(N), radical_inverse_VdC(i)); +} + +vec3 importance_sample_GGX(vec2 sample_box, vec3 N, float roughness) { + float r2 = roughness * roughness; + + // Spherical coordinates. + float phi = 2.0 * PI * sample_box.x; + float cos_theta = sqrt((1.0 - sample_box.y) / (1.0 + (r2*r2 - 1.0) * sample_box.y)); + float sin_theta = sqrt(1.0 - cos_theta * cos_theta); + + // Map spherical coordinates to Cartesian coordinates in tangent space. + vec3 H = vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta); + + // Map from tangent space to world space. + // + // Tangent space: + // + // N + // | + // | + // | + // |_ _ _ _ _ B + // / + // / + // T + vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 T = normalize(cross(up,N)); + vec3 B = cross(N,T); + vec3 H_ws = H.x*T + H.y*B + H.z*N; + return H_ws; +} + +float geometry_schlick_GGX(float k, float NdotV) { + return NdotV / (NdotV * (1.0 - k) + k); +} + +float geometry_smith(float roughness, float NdotL, float NdotV) { + float k = roughness * roughness / 2.0; // IBL + return geometry_schlick_GGX(k, NdotV) * geometry_schlick_GGX(k, NdotL); +} + +vec2 integrate_brdf(float NdotV, float roughness) +{ + vec3 V = vec3(sqrt(1.0 - NdotV * NdotV), 0.0, NdotV); + vec3 N = vec3(0.0, 0.0, 1.0); + + float scale = 0.0; + float bias = 0.0; + for (int i = 0; i < NUM_SAMPLES; ++i) { + vec2 sample_box = hammersley(i, NUM_SAMPLES); + vec3 H = importance_sample_GGX(sample_box, N, roughness); + vec3 L = reflect(-V,H); + float NdotL = max(0.0, L.z); + + if (NdotL > 0.0) { + float NdotH = max(0.0, H.z); + float VdotH = max(0.0, dot(V,H)); + float G = geometry_smith(roughness, NdotL, NdotV); + float G_vis = (G * VdotH) / (NdotH * NdotV); + float Fc = pow(1.0 - VdotH, 5.0); + scale += (1.0 - Fc) * G_vis; + bias += Fc * G_vis; + } + } + scale /= float(NUM_SAMPLES); + bias /= float(NUM_SAMPLES); + return vec2(scale, bias); +} + +void main() +{ + Color = integrate_brdf(Texcoord.x, Texcoord.y); +} diff --git a/shaders/cook_torrance.frag b/shaders/cook_torrance.frag new file mode 100644 index 0000000..1975491 --- /dev/null +++ b/shaders/cook_torrance.frag @@ -0,0 +1,255 @@ +precision highp float; + +uniform vec4 BaseColorFactor; +uniform float MetallicFactor; +uniform float RoughnessFactor; +uniform vec3 EmissiveFactor; + +#ifdef HAS_ALBEDO_MAP +uniform sampler2D BaseColorTexture; +#endif +#ifdef HAS_METALLIC_ROUGHNESS_MAP +uniform sampler2D MetallicRoughnessTexture; +#endif +#ifdef HAS_EMISSIVE_MAP +uniform sampler2D EmissiveTexture; +#endif +#ifdef HAS_OCCLUSION_MAP +uniform sampler2D AmbientOcclusionTexture; +#endif +#ifdef HAS_NORMAL_MAP +uniform sampler2D NormalMap; +#endif + +// TODO: Handle case in which there is no sky. Pass a boolean. +uniform samplerCube Sky; +uniform samplerCube IrradianceMap; +uniform samplerCube PrefilteredEnvironmentMap; +uniform sampler2D BRDFIntegrationMap; +uniform float MaxReflectionLOD; + +uniform vec3 CameraPosition; // World space. + +// World-space position, normal and tangent. +in vec3 Position; +#ifdef HAS_NORMALS +in vec3 Normal; +#endif +#ifdef HAS_TANGENTS +in vec4 Tangent; +#endif +#ifdef HAS_TEXCOORDS +in vec2 Texcoord; +#endif + +layout (location = 0) out vec4 Colour; + +#define PI 3.1415926535897932384626433832795 +#define INV_PI 0.3183098861837907 + +/// Transform a normal map sample into world space. +/// +/// |normalWs| is the surface normal in world space. +/// |normalMapSample| is the normal map sample, not necessarily normalized. +/// +/// TODO: Move to "normal.h" +#if defined(HAS_NORMAL_MAP) && (defined(HAS_TANGENTS) || defined(HAS_TEXCOORDS)) +vec3 get_ws_normal(vec3 normalWs, vec3 normalMapSample) { + vec3 N = normalize(Normal); +#ifdef HAS_TANGENTS + //vec3 T = normalize(tangent.xyz - dot(tangent.xyz, N) * N); + vec3 T = Tangent.xyz; + vec3 B = Tangent.w * cross(N, T); +#elif HAS_TEXCOORDS // No tangents, but must have texcoords. + vec3 pos_dx = dFdx(Position); + vec3 pos_dy = dFdy(Position); + // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); + // vec3 uv_dy = vec3(dFdy(Texcoord), 0.0); + vec3 uv_dx = dFdx(vec3(Texcoord, 0.0)); + vec3 uv_dy = dFdy(vec3(Texcoord, 0.0)); + vec3 T = (uv_dy.t * pos_dx - uv_dx.t * pos_dy) / + (uv_dx.s * uv_dy.t - uv_dy.s * uv_dx.t); + // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; + T = normalize(T - dot(T, N) * N); + vec3 B = normalize(cross(N, T)); +#endif + + if (gl_FrontFacing == false) { + T = -T; + B = -B; + N = -N; + } + + vec3 s = normalMapSample; + //return normalize(s.x * T + s.y * B + s.z * N); + return normalize(mat3(T,B,N) * s); +} +#endif // HAS_TANGENTS || HAS_TEXCOORDS + +float trowbridge_reitz_GGX(float roughness, float NdotH) { + float a = roughness * roughness; + float a2 = a * a; + float d = NdotH * NdotH * (a2 - 1.0) + 1.0; + return a2 / (PI * d * d); +} + +float geometry_schlick_GGX(float k, float NdotV) { + return NdotV / (NdotV * (1.0 - k) + k); +} + +float geometry_smith(float roughness, float NdotL, float NdotV) { + float k = roughness * roughness / 2.0; // IBL + return geometry_schlick_GGX(k, NdotV) * geometry_schlick_GGX(k, NdotL); +} + +vec3 fresnel_schlick(vec3 F0, float HdotV) { + return F0 + (1.0 - F0) * pow(clamp(1.0 - HdotV, 0.0, 1.0), 5.0); +} + +vec3 fresnel_schlick_roughness(vec3 F0, float NdotV, float roughness) { + return F0 + + (max(vec3(1.0 - roughness), F0) - F0) + * pow(clamp(1.0 - NdotV, 0.0, 1.0), 5.0); +} + +// Cook-Torrance BRDF for a single light direction. +vec3 cook_torrance( + vec3 albedo, float metallic, float roughness, + float NdotL, float NdotV, float NdotH, float HdotV) { + vec3 F0 = mix(vec3(0.04), albedo, metallic); + float D = trowbridge_reitz_GGX(roughness, NdotH); + vec3 F = fresnel_schlick(F0, HdotV); + float G = geometry_smith(roughness, NdotL, NdotV); + vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); + vec3 diffuse = Kd*albedo*INV_PI; + // Take a max to prevent division by 0 when either dot product is 0. + vec3 specular = (D*F*G) / max(4.0 * NdotV * NdotL, 0.0001); + return diffuse + specular; +} + +// Cook-Torrance BRDF for IBL. +vec3 cook_torrance_IBL( + vec3 albedo, float metallic, float roughness, float occlusion, + float NdotV, + vec3 irradiance, vec3 prefiltered_env, vec2 BRDF_env) { + vec3 F0 = mix(vec3(0.04), albedo, metallic); + vec3 F = fresnel_schlick_roughness(F0, NdotV, roughness); + vec3 Kd = mix(vec3(1.0) - F, vec3(0.0), metallic); + vec3 diffuse = Kd * albedo * INV_PI * irradiance; + vec3 specular = prefiltered_env * (F * BRDF_env.x + BRDF_env.y); + return occlusion * (diffuse + specular); +} + +void main() +{ + // TODO: Also use the specular F0 map from the model, and emissive. Make sure + // to use all maps. + // https://sketchfab.com/models/b81008d513954189a063ff901f7abfe4 +#ifdef HAS_NORMAL_MAP + vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; + vec3 N = get_ws_normal(Normal, normalMapSample); +#elif HAS_NORMALS + vec3 N = normalize(Normal); +#endif + vec3 V = normalize(CameraPosition - Position); + vec3 R = reflect(-V, N); + // Not needed for IBL. + //vec3 L = N; + //vec3 H = normalize(L + V); + + float NdotV = max(0.0, dot(N, V)); + // Not needed for IBL. + //float NdotL = max(0.0, dot(N,L)); + //float NdotH = max(0.0, dot(N,H)); + //float HdotV = clamp(dot(H,V), 0.0, 1.0); // Clamp to prevent black spots. + + // TODO: BaseColorFactor and BaseColorTexture are vec4/rgba quantities + // respectively. Handle the alpha channel. + // TODO: Other factors. +#ifdef HAS_ALBEDO_MAP + vec3 albedo = vec3(BaseColorFactor) * texture(BaseColorTexture, Texcoord).rgb; +#else + vec3 albedo = vec3(BaseColorFactor); +#endif +#ifdef HAS_METALLIC_ROUGHNESS_MAP + // Spec: "Its green channel contains roughness values and its blue channel + // contains metalness values." + vec2 metal_roughness + = vec2(MetallicFactor, RoughnessFactor) + * texture(MetallicRoughnessTexture, Texcoord).bg; +#else + vec2 metal_roughness = vec2(MetallicFactor, RoughnessFactor); +#endif +#ifdef HAS_EMISSIVE_MAP + vec3 emissive = EmissiveFactor * texture(EmissiveTexture, Texcoord).rgb; +#else + vec3 emissive = EmissiveFactor; +#endif +#ifdef HAS_OCCLUSION_MAP + float occlusion = texture(AmbientOcclusionTexture, Texcoord).r; +#else + float occlusion = 1.0; +#endif + float metallic = metal_roughness.x; + float roughness = metal_roughness.y; + + // For a single light direction: + // vec3 brdf = cook_torrance(albedo, metallic, roughness, NdotL, NdotV, NdotH, HdotV); + // vec3 Li = texture(Sky, N).rgb; + // vec3 colour = brdf * Li * NdotL; + + // For IBL: + vec3 irradiance = texture(IrradianceMap, N).rgb; + vec3 prefiltered_env = textureLod(PrefilteredEnvironmentMap, R, roughness * MaxReflectionLOD).rgb; + vec2 BRDF_env = texture(BRDFIntegrationMap, vec2(NdotV, roughness)).rg; + vec3 colour = cook_torrance_IBL( + albedo, metallic, roughness, occlusion, NdotV, irradiance, prefiltered_env, BRDF_env); + + colour += emissive; + + // Reinhard tone mapping. + colour = colour / (colour + vec3(1.0)); + + // Gamma correction. + colour = pow(colour, vec3(1.0 / 2.2)); + + // Debugging. + // + // Normal texture. + //colour = normalMapSample * 0.5 + 0.5; + // + // Geometry normal. + //colour = normalize(Normal) * 0.5 + 0.5; + // + // Shading normal. + //colour = N * 0.5 + 0.5; + // + // Tangent and bitangent. + // { + // vec3 pos_dx = dFdx(Position); + // vec3 pos_dy = dFdy(Position); + // // vec3 uv_dx = vec3(dFdx(Texcoord), 0.0); + // // vec3 uv_dy = vec3(dFdy(Texcoord), 0.0); + // vec3 uv_dx = dFdx(vec3(Texcoord, 0.0)); + // vec3 uv_dy = dFdy(vec3(Texcoord, 0.0)); + // vec3 T = (uv_dy.t * pos_dx - uv_dx.t * pos_dy) / + // (uv_dx.s * uv_dy.t - uv_dy.s * uv_dx.t); + // // vec3 T = pos_dx * uv_dy.t - pos_dy * uv_dx.t; + // vec3 N = normalize(Normal); + // T = normalize(T - dot(T, N) * N); + // vec3 B = normalize(cross(N, T)); + + // if (gl_FrontFacing == false) { + // T = -T; + // B = -B; + // N = -N; + // } + + // // Tangent. + // //colour = T * 0.5 + 0.5; + // // Bitangent. + // //colour = B * 0.5 + 0.5; + // } + + Colour = vec4(colour, 1.0); +} diff --git a/shaders/cook_torrance.vert b/shaders/cook_torrance.vert new file mode 100644 index 0000000..5f126c0 --- /dev/null +++ b/shaders/cook_torrance.vert @@ -0,0 +1,75 @@ +precision highp float; + +uniform mat4 ModelMatrix; +// uniform mat4 Modelview; +uniform mat4 View; +uniform mat4 Projection; +//uniform mat4 MVP; +#ifdef HAS_JOINTS +// The client should pass in an appropriate value for MAX_JOINTS. +// #define MAX_JOINTS 96 +// +// matnxm -- n columns and m rows, different convention from math. +// We don't need the last row of [0, 0, 0, 1], so drop it to pack the matrices +// as tightly as possible. +// 256 joints * 4x4 matrix * 4 bytes/float = 16.0KB +// 256 joints * 4x3 matrix * 4 bytes/float = 12.0KB +// 96 joints * 4x4 matrix * 4 bytes/float = 6.0KB +// 96 joints * 4x3 matrix * 4 bytes/float = 4.5KB +//uniform mat4x3 Joints[MAX_JOINTS]; +uniform mat4 JointMatrices[MAX_JOINTS]; // Use 4x4 for now to keep it simple. +#endif + +layout (location = 0) in vec3 vPosition; +#ifdef HAS_NORMALS +layout (location = 1) in vec3 vNormal; +#endif +#ifdef HAS_TANGENTS +layout (location = 2) in vec4 vTangent; +#endif +#ifdef HAS_TEXCOORDS +layout (location = 3) in vec2 vTexcoord; +#endif +#ifdef HAS_JOINTS +layout (location = 4) in uvec4 vJoint; +layout (location = 5) in vec4 vWeight; +#endif + +// World-space position, normal and tangent. +out vec3 Position; +#ifdef HAS_NORMALS +out vec3 Normal; +#endif +#ifdef HAS_TANGENTS +out vec4 Tangent; +#endif +#ifdef HAS_TEXCOORDS +out vec2 Texcoord; +#endif + +void main() +{ +#ifdef HAS_JOINTS + mat4 skinMatrix = + vWeight.x * JointMatrices[vJoint.x] + + vWeight.y * JointMatrices[vJoint.y] + + vWeight.z * JointMatrices[vJoint.z] + + vWeight.w * JointMatrices[vJoint.w]; + Position = vec3(ModelMatrix * skinMatrix * vec4(vPosition, 1.0)); +#else + Position = vec3(ModelMatrix * vec4(vPosition, 1.0)); +#endif +#ifdef HAS_NORMALS + Normal = mat3(ModelMatrix) * vNormal; + //Normal = normalize(ModelMatrix * vec4(vNormal, 0.0)).xyz; +#endif +#ifdef HAS_TANGENTS + Tangent = vec4(mat3(ModelMatrix) * vTangent.xyz, vTangent.w); +#endif +#ifdef HAS_TEXCOORDS + Texcoord = vTexcoord; +#endif + gl_Position = Projection * View * vec4(Position, 1.0); + //gl_Position = Projection * vec4(Position, 1.0); + //gl_Position = MVP * vec4(vPosition, 1.0); +} diff --git a/shaders/cubemap_filtering.vert b/shaders/cubemap_filtering.vert new file mode 100644 index 0000000..d0cf73f --- /dev/null +++ b/shaders/cubemap_filtering.vert @@ -0,0 +1,39 @@ +precision highp float; + +#define PI 3.1415926535897932384626433832795 +#define FOVY (90.0 * PI / 180.0) + +uniform mat4 CameraRotation; // From camera space to world space. +uniform float Flip; + +layout (location = 0) in vec2 vPosition; + +out vec3 Ray; + +// DEBUG +// out vec2 Texcoord; + +// This is very similar to the skyquad vertex shader. +// +// The ray is not normalized because it isn't necessary for cubemap sampling. +// +// We also use a fixed fovy = 90 degrees because we want the frustum to pass +// exactly through each face of the cube. The aspect ratio is also just 1. +vec3 sky_ray(vec2 FilmPosition) +{ + float d = 0.5 / tan(FOVY/2.0); + return vec3(FilmPosition, -d); +} + +void main() +{ + vec2 FilmPosition = vPosition * 0.5; // map [-1,1] -> [-1/2, +1/2] + FilmPosition *= Flip; + Ray = mat3(CameraRotation) * sky_ray(FilmPosition); + // TODO: Should disable depth test when rendering. + gl_Position = vec4(vPosition, 0.99999, 1.0); // z=1 -> send to background + + // DEBUG + // Texcoord = FilmPosition + 0.5; + // Texcoord.y = 1.0 - Texcoord.y; +} diff --git a/shaders/debug3d.frag b/shaders/debug3d.frag new file mode 100644 index 0000000..54568d4 --- /dev/null +++ b/shaders/debug3d.frag @@ -0,0 +1,21 @@ +precision highp float; + +const vec3 Colour = vec3(0.97, 0.948, 0.91); + +const vec3 ToLight = vec3(1, 0.7, 1); + +in vec3 Normal; + +out vec4 FragColour; + +void main() +{ + vec3 N = normalize(Normal); + vec3 L = normalize(ToLight); + + vec3 a = vec3(0.7); + vec3 d = vec3(0.3 * (0.7*max(0.0, dot(L,N)) + 0.3*max(0.0, dot(-L,N)))); + vec3 c = Colour * (a+d); + + FragColour = vec4(pow(c, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/debug3d.vert b/shaders/debug3d.vert new file mode 100644 index 0000000..d51684f --- /dev/null +++ b/shaders/debug3d.vert @@ -0,0 +1,15 @@ +precision highp float; + +uniform mat4 Modelview; +uniform mat4 Projection; + +layout (location = 0) in vec3 vPosition; +layout (location = 1) in vec3 vNormal; + +out vec3 Normal; + +void main() +{ + Normal = mat3(Modelview) * vNormal; + gl_Position = Projection * Modelview * vec4(vPosition, 1.0); +} diff --git a/shaders/immediate_mode.frag b/shaders/immediate_mode.frag new file mode 100644 index 0000000..ac23b5c --- /dev/null +++ b/shaders/immediate_mode.frag @@ -0,0 +1,10 @@ +precision highp float; + +uniform vec4 Colour; + +out vec4 FragColour; + +void main() +{ + FragColour = vec4(pow(Colour.rgb, vec3(1.0/2.2)), Colour.a); +} diff --git a/shaders/immediate_mode.vert b/shaders/immediate_mode.vert new file mode 100644 index 0000000..65070bb --- /dev/null +++ b/shaders/immediate_mode.vert @@ -0,0 +1,11 @@ +precision highp float; + +uniform mat4 Model; +uniform mat4 ViewProjection; + +layout (location = 0) in vec3 vPosition; + +void main() +{ + gl_Position = ViewProjection * Model * vec4(vPosition, 1.0); +} diff --git a/shaders/irradiance_map.frag b/shaders/irradiance_map.frag new file mode 100644 index 0000000..8200e73 --- /dev/null +++ b/shaders/irradiance_map.frag @@ -0,0 +1,65 @@ +precision highp float; + +#define PI 3.1415926535897932384626433832795 +#define EPS 0.001 +#define NUM_SAMPLES_AZIMUTH 250 +#define NUM_SAMPLES_ZENITH 50 +#define MAX_AZIMUTH (2*PI) +#define MAX_ZENITH (PI/2.0) +#define AZIMUTH_DELTA (MAX_AZIMUTH / float(NUM_SAMPLES_AZIMUTH)) +#define ZENITH_DELTA (MAX_ZENITH / float(NUM_SAMPLES_ZENITH)) + +uniform samplerCube Sky; + +in vec3 Ray; + +// DEBUG +// in vec2 Texcoord; + +layout (location = 0) out vec4 Color; + +void main() +{ + // Tangent space: + // + // N + // | + // | + // | + // |_ _ _ _ _ B + // / + // / + // T + vec3 N = normalize(Ray); + vec3 B = (abs(N.x) - 1.0 <= EPS) ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 T = normalize(cross(B, N)); + B = normalize(cross(N, T)); + + int num_samples = 0; + vec3 irradiance = vec3(0.0); + for (float theta = 0.0; theta < MAX_AZIMUTH; theta += AZIMUTH_DELTA) { + for (float phi = 0.0; phi < MAX_ZENITH; phi += ZENITH_DELTA) { + // Spherical to Cartesian. + vec3 sample_tangent_space = vec3( + sin(phi) * cos(theta), + sin(phi) * sin(theta), + cos(phi)); + // Tangent space to world space. + vec3 sample_world_space = + sample_tangent_space.x * B + + sample_tangent_space.y * T + + sample_tangent_space.z * N; + + irradiance += texture(Sky, sample_world_space).rgb * sin(phi) * cos(phi); + num_samples += 1; + } + } + irradiance = PI * irradiance / float(num_samples); + + // For debugging in trace. + //irradiance = texture(Sky, Ray).rgb; + // irradiance = vec3(Texcoord, 0.0); + //irradiance = pow(irradiance, vec3(1.0/2.2)); + + Color = vec4(irradiance, 1.0); +} diff --git a/shaders/prefiltered_environment_map.frag b/shaders/prefiltered_environment_map.frag new file mode 100644 index 0000000..8327950 --- /dev/null +++ b/shaders/prefiltered_environment_map.frag @@ -0,0 +1,78 @@ +precision highp float; + +#define PI 3.1415926535897932384626433832795 +#define NUM_SAMPLES 4096 + +uniform samplerCube Sky; +uniform float Roughness; + +in vec3 Ray; + +layout (location = 0) out vec4 Color; + +float radical_inverse_VdC(uint bits) { + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); + return float(bits) * 2.3283064365386963e-10; // / 0x100000000 +} + +vec2 hammersley(uint i, uint N) { + return vec2(float(i)/float(N), radical_inverse_VdC(i)); +} + +vec3 importance_sample_GGX(vec2 sample_box, vec3 N, float roughness) { + float r2 = roughness * roughness; + + // Spherical coordinates. + float phi = 2.0 * PI * sample_box.x; + float cos_theta = sqrt((1.0 - sample_box.y) / (1.0 + (r2*r2 - 1.0) * sample_box.y)); + float sin_theta = sqrt(1.0 - cos_theta * cos_theta); + + // Map spherical coordinates to Cartesian coordinates in tangent space. + vec3 H = vec3(cos(phi) * sin_theta, sin(phi) * sin_theta, cos_theta); + + // Map from tangent space to world space. + // + // Tangent space: + // + // N + // | + // | + // | + // |_ _ _ _ _ B + // / + // / + // T + vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 T = normalize(cross(up,N)); + vec3 B = cross(N,T); + vec3 H_ws = H.x*T + H.y*B + H.z*N; + return H_ws; +} + +void main() +{ + vec3 N = normalize(Ray); + vec3 R = N; + vec3 V = R; + + vec3 irradiance = vec3(0.0); + float total_weight = 0.0; + for (uint i = 0; i < NUM_SAMPLES; ++i) { + vec2 sample_box = hammersley(i, NUM_SAMPLES); + vec3 H = importance_sample_GGX(sample_box, N, Roughness); + vec3 L = reflect(-V,H); + + float NdotL = max(dot(N,L), 0.0); + if (NdotL > 0.0) { + irradiance += texture(Sky, H).rgb * NdotL; + total_weight += NdotL; + } + } + irradiance /= total_weight; + + Color = vec4(irradiance, 1.0); +} diff --git a/shaders/quad.vert b/shaders/quad.vert new file mode 100644 index 0000000..ef1834c --- /dev/null +++ b/shaders/quad.vert @@ -0,0 +1,9 @@ +layout (location = 0) in vec2 Position; // Assuming Position in [-1,1]. + +out vec2 Texcoord; + +void main() +{ + Texcoord = Position * 0.5 + 0.5; + gl_Position = vec4(Position, 0.0, 1.0); +} diff --git a/shaders/skyquad.frag b/shaders/skyquad.frag new file mode 100644 index 0000000..9b44bfd --- /dev/null +++ b/shaders/skyquad.frag @@ -0,0 +1,13 @@ +uniform samplerCube Skyquad; + +in vec3 Ray; + +layout (location = 0) out vec4 Colour; + +void main() +{ + vec3 R = normalize(Ray); + Colour = vec4(pow(texture(Skyquad, R).rgb, vec3(1.0/2.2)), 1.0); + // Debug. + //Colour = vec4(pow(R*0.5 + 0.5, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/skyquad.vert b/shaders/skyquad.vert new file mode 100644 index 0000000..c0c46e6 --- /dev/null +++ b/shaders/skyquad.vert @@ -0,0 +1,27 @@ +uniform mat4 CameraRotation; // From camera space to world space. +uniform float Fovy; +uniform float Aspect; + +layout (location = 0) in vec2 Position; + +out vec3 Ray; + +// We would typically normalize the vector, but there is no need to do so when +// sampling a cube map. +vec3 sky_ray(vec2 FilmPosition) +{ + //float d = 0.5 / tan(Fovy/2.0); + // return vec3((FilmPosition.x - 0.5) * Aspect, + // FilmPosition.y - 0.5, + // -d); + float d = 0.5 / tan(Fovy/2.0); + return vec3(FilmPosition, -d); +} + +void main() +{ + vec2 FilmPosition = Position * 0.5; // map [-1,1] -> [-1/2, +1/2] + Ray = mat3(CameraRotation) * sky_ray(FilmPosition); + // Set z to the background. + gl_Position = vec4(Position, 0.99999, 1.0); // z=1 -> send to background +} diff --git a/shaders/view_normal_mapped_normals.frag b/shaders/view_normal_mapped_normals.frag new file mode 100644 index 0000000..a372c02 --- /dev/null +++ b/shaders/view_normal_mapped_normals.frag @@ -0,0 +1,28 @@ +precision highp float; + +uniform sampler2D NormalMap; + +in vec3 Normal; +in vec4 Tangent; +in vec3 Bitangent; // TODO: Compute here or in PS? +in vec2 Texcoord; + +out vec4 FragColour; + +// TODO: Move to "normal.h" +vec3 get_vs_normal(vec3 normal, vec4 tangent, vec3 normalMapSample) { + vec3 N = normal; + // vec3 T = normalize(interpTangent - dot(tangent, N)*N); + // vec3 B = normalize(interpBitangent - dot(interpTangent, N)*N - dot(interpTangent, T)*T); + vec3 T = tangent.xyz; + //vec3 B = tangent.w * cross(N,T); + vec3 B = Bitangent; + vec3 n = normalMapSample; + return normalize(n.x*T + n.y*B + n.z*N); +} + +void main() { + vec3 normalMapSample = texture(NormalMap, Texcoord).xyz * 2.0 - 1.0; + vec3 N = get_vs_normal(Normal, Tangent, normalMapSample); + FragColour = vec4(pow(N, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/view_normal_mapped_normals.vert b/shaders/view_normal_mapped_normals.vert new file mode 100644 index 0000000..004ed9a --- /dev/null +++ b/shaders/view_normal_mapped_normals.vert @@ -0,0 +1,22 @@ +precision highp float; + +uniform mat4 Modelview; +uniform mat4 Projection; + +layout (location = 0) in vec3 vPosition; +layout (location = 1) in vec3 vNormal; +layout (location = 2) in vec4 vTangent; +layout (location = 3) in vec2 vTexcoord; + +out vec3 Normal; +out vec4 Tangent; +out vec3 Bitangent; // TODO: Compute here or in PS? +out vec2 Texcoord; + +void main() { + Texcoord = vTexcoord; + Normal = mat3(Modelview) * vNormal; + Tangent = vec4(mat3(Modelview) * vTangent.xyz, vTangent.w); + Bitangent = cross(Normal, Tangent.xyz) * vTangent.w; + gl_Position = Projection * Modelview * vec4(vPosition, 1.0); +} diff --git a/shaders/view_normals.frag b/shaders/view_normals.frag new file mode 100644 index 0000000..e90189c --- /dev/null +++ b/shaders/view_normals.frag @@ -0,0 +1,11 @@ +precision highp float; + +in vec3 Normal; + +out vec4 FragColour; + +void main() +{ + vec3 N = normalize(Normal); + FragColour = vec4(pow(N, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/view_normals.vert b/shaders/view_normals.vert new file mode 100644 index 0000000..d51684f --- /dev/null +++ b/shaders/view_normals.vert @@ -0,0 +1,15 @@ +precision highp float; + +uniform mat4 Modelview; +uniform mat4 Projection; + +layout (location = 0) in vec3 vPosition; +layout (location = 1) in vec3 vNormal; + +out vec3 Normal; + +void main() +{ + Normal = mat3(Modelview) * vNormal; + gl_Position = Projection * Modelview * vec4(vPosition, 1.0); +} diff --git a/shaders/view_tangents.frag b/shaders/view_tangents.frag new file mode 100644 index 0000000..11d1455 --- /dev/null +++ b/shaders/view_tangents.frag @@ -0,0 +1,17 @@ +precision highp float; + +in vec4 Tangent; + +out vec4 FragColour; + +void main() { + vec3 T = normalize(Tangent.xyz); + FragColour = vec4(pow(T, vec3(1.0 / 2.2)), 1.0);FragColour = vec4(pow(T, vec3(1.0 / 2.2)), 1.0); + + // View sign. + // float sign = Tangent.w; + // float R = sign > 0.0 ? 1.0 : 0.0; + // float B = sign < 0.0 ? 1.0 : 0.0; + // vec3 signColour = vec3(R,0.0,B); + // FragColour = vec4(pow(signColour, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/view_tangents.vert b/shaders/view_tangents.vert new file mode 100644 index 0000000..561ad22 --- /dev/null +++ b/shaders/view_tangents.vert @@ -0,0 +1,14 @@ +precision highp float; + +uniform mat4 Modelview; +uniform mat4 Projection; + +layout (location = 0) in vec3 vPosition; +layout (location = 2) in vec4 vTangent; + +out vec4 Tangent; + +void main() { + Tangent = vec4(Tangent.xyz = mat3(Modelview) * vTangent.xyz, vTangent.w); + gl_Position = Projection * Modelview * vec4(vPosition, 1.0); +} diff --git a/shaders/view_texture.frag b/shaders/view_texture.frag new file mode 100644 index 0000000..12fa367 --- /dev/null +++ b/shaders/view_texture.frag @@ -0,0 +1,15 @@ +uniform sampler2D Texture; + +in vec2 Texcoord; + +layout (location = 0) out vec4 Colour; + +void main() +{ + // There is a question here whether we want to view the texture through the + // sampler or unfiltered. Prefer the latter for now. + //vec3 colour = texture(Texture, Texcoord).rgb; + ivec2 st = ivec2(Texcoord * vec2(textureSize(Texture, 0))); + vec3 colour = texelFetch(Texture, st, 0).rgb; + Colour = vec4(pow(colour, vec3(1.0 / 2.2)), 1.0); +} diff --git a/shaders/view_texture.vert b/shaders/view_texture.vert new file mode 100644 index 0000000..4e3c7d7 --- /dev/null +++ b/shaders/view_texture.vert @@ -0,0 +1,13 @@ +layout (location = 0) in vec2 Position; + +out vec2 Texcoord; + +void main() +{ + Texcoord = Position * vec2(0.5) + vec2(0.5);// Map from [-1, +1] to [0, 1]. + // The Gfx library is written to work with the glTF sample models, which + // seem to want the textures loaded "as is" without flipping. Flip the + // y-coordinate here so that the texture appears as expected. + Texcoord.y = 1.0 - Texcoord.y; + gl_Position = vec4(Position, 0.0, 1.0); +} -- cgit v1.2.3