add infiniteGrid, rewrote the shadow , now the shadow is a simple hardware shadow
This commit is contained in:
BIN
Editor/assets/editor/StopButton.png
Normal file
BIN
Editor/assets/editor/StopButton.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 115 B |
@ -1,202 +1,49 @@
|
||||
Scene: Scene Name
|
||||
Environment:
|
||||
AssetPath: assets\env\birchwood_4k.hdr
|
||||
AssetHandle: 10549690553241162923
|
||||
Light:
|
||||
Direction: [-0.787, -0.733, 1]
|
||||
Radiance: [1, 1, 1]
|
||||
Multiplier: 0.515
|
||||
Direction: [-0.314, -0.941, -0.209]
|
||||
Radiance: [0, 0, 0]
|
||||
Multiplier: 1
|
||||
Entities:
|
||||
- Entity: 3247025703490125974
|
||||
- Entity: 17803125207910630398
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Player
|
||||
Tag: Directional Light
|
||||
TransformComponent:
|
||||
Position: [2.8080375, 1.5, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [2, 2, 2]
|
||||
ScriptComponent:
|
||||
ModuleName: FPSExample.FPSPlayer
|
||||
StoredFields:
|
||||
- Name: WalkingSpeed
|
||||
Type: 1
|
||||
Data: 4
|
||||
- Name: RunSpeed
|
||||
Type: 1
|
||||
Data: 10
|
||||
- Name: JumpForce
|
||||
Type: 1
|
||||
Data: 1
|
||||
- Name: MouseSensitivity
|
||||
Type: 1
|
||||
Data: 10
|
||||
- Name: Distance
|
||||
Type: 1
|
||||
Data: 0
|
||||
MeshComponent:
|
||||
AssetPath: assets/meshes/Capsule.fbx
|
||||
RigidBodyComponent:
|
||||
BodyType: 1
|
||||
Mass: 1
|
||||
IsKinematic: false
|
||||
Layer: 1
|
||||
Constraints:
|
||||
LockPositionX: false
|
||||
LockPositionY: false
|
||||
LockPositionZ: false
|
||||
LockRotationX: true
|
||||
LockRotationY: true
|
||||
LockRotationZ: true
|
||||
PhysicsMaterialComponent:
|
||||
StaticFriction: 0.1
|
||||
DynamicFriction: 0.1
|
||||
Bounciness: 0.1
|
||||
MeshColliderComponent:
|
||||
AssetPath: assets/meshes/Capsule.fbx
|
||||
IsConvex: true
|
||||
IsTrigger: false
|
||||
- Entity: 11149966982516343187
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [-0.4810984, -0.20606127, 2.9545484]
|
||||
Scale: [1.0000023, 1.0000007, 0.9999998]
|
||||
DirectionalLightComponent:
|
||||
Radiance: [1, 1, 1]
|
||||
CastShadows: true
|
||||
SoftShadows: true
|
||||
LightSize: 0.5
|
||||
- Entity: 4315886439647742331
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Mesh Collider
|
||||
Tag: Cube
|
||||
TransformComponent:
|
||||
Position: [-2.6045518, 1, -0.0017139912]
|
||||
Position: [0, 2.048974, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
MeshComponent:
|
||||
AssetPath: assets/meshes/Sphere1m.fbx
|
||||
RigidBodyComponent:
|
||||
BodyType: 1
|
||||
Mass: 0.1
|
||||
IsKinematic: false
|
||||
Layer: 1
|
||||
Constraints:
|
||||
LockPositionX: false
|
||||
LockPositionY: false
|
||||
LockPositionZ: false
|
||||
LockRotationX: false
|
||||
LockRotationY: false
|
||||
LockRotationZ: false
|
||||
PhysicsMaterialComponent:
|
||||
StaticFriction: 1
|
||||
DynamicFriction: 1
|
||||
Bounciness: 0.1
|
||||
MeshColliderComponent:
|
||||
AssetPath: assets/meshes/Sphere1m.fbx
|
||||
IsConvex: true
|
||||
IsTrigger: false
|
||||
- Entity: 10169503531257462571
|
||||
AssetID: 3580169978473467053
|
||||
- Entity: 16992665426857995732
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Box
|
||||
TransformComponent:
|
||||
Position: [0, 1.5, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [2, 2, 2]
|
||||
MeshComponent:
|
||||
AssetPath: assets/meshes/Cube1m.fbx
|
||||
RigidBodyComponent:
|
||||
BodyType: 1
|
||||
Mass: 0.5
|
||||
IsKinematic: false
|
||||
Layer: 1
|
||||
Constraints:
|
||||
LockPositionX: false
|
||||
LockPositionY: false
|
||||
LockPositionZ: false
|
||||
LockRotationX: false
|
||||
LockRotationY: false
|
||||
LockRotationZ: false
|
||||
PhysicsMaterialComponent:
|
||||
StaticFriction: 1
|
||||
DynamicFriction: 1
|
||||
Bounciness: 0
|
||||
BoxColliderComponent:
|
||||
Offset: [0, 0, 0]
|
||||
Size: [1, 1, 1]
|
||||
IsTrigger: false
|
||||
- Entity: 14057422478420564497
|
||||
TagComponent:
|
||||
Tag: Sphere
|
||||
TransformComponent:
|
||||
Position: [-3.9876995, 1, -1.9669533e-06]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
MeshComponent:
|
||||
AssetPath: assets/meshes/Sphere1m.fbx
|
||||
RigidBodyComponent:
|
||||
BodyType: 1
|
||||
Mass: 1
|
||||
IsKinematic: false
|
||||
Layer: 1
|
||||
Constraints:
|
||||
LockPositionX: false
|
||||
LockPositionY: false
|
||||
LockPositionZ: false
|
||||
LockRotationX: false
|
||||
LockRotationY: false
|
||||
LockRotationZ: false
|
||||
PhysicsMaterialComponent:
|
||||
StaticFriction: 0.1
|
||||
DynamicFriction: 0.1
|
||||
Bounciness: 0.1
|
||||
SphereColliderComponent:
|
||||
Radius: 0.5
|
||||
IsTrigger: false
|
||||
- Entity: 5178862374589434728
|
||||
TagComponent:
|
||||
Tag: Camera
|
||||
TransformComponent:
|
||||
Position: [2.808, 2.25, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
CameraComponent:
|
||||
Camera:
|
||||
ProjectionType: 0
|
||||
PerspectiveFOV: 65
|
||||
PerspectiveNear: 0.100000001
|
||||
PerspectiveFar: 1000
|
||||
OrthographicSize: 10
|
||||
OrthographicNear: -1
|
||||
OrthographicFar: 1
|
||||
Primary: true
|
||||
- Entity: 18306113171518048249
|
||||
TagComponent:
|
||||
Tag: Box
|
||||
Tag: Cube
|
||||
TransformComponent:
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [50, 1, 50]
|
||||
MeshComponent:
|
||||
AssetPath: assets/meshes/Cube1m.fbx
|
||||
RigidBodyComponent:
|
||||
BodyType: 0
|
||||
Mass: 1
|
||||
IsKinematic: false
|
||||
Layer: 1
|
||||
Constraints:
|
||||
LockPositionX: false
|
||||
LockPositionY: false
|
||||
LockPositionZ: false
|
||||
LockRotationX: false
|
||||
LockRotationY: false
|
||||
LockRotationZ: false
|
||||
PhysicsMaterialComponent:
|
||||
StaticFriction: 1
|
||||
DynamicFriction: 1
|
||||
Bounciness: 0
|
||||
BoxColliderComponent:
|
||||
Offset: [0, 0, 0]
|
||||
Size: [1, 1, 1]
|
||||
IsTrigger: false
|
||||
- Entity: 2025484417758554619
|
||||
TagComponent:
|
||||
Tag: Sky Light
|
||||
TransformComponent:
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
SkyLightComponent:
|
||||
EnvironmentAssetPath: assets/env/birchwood_4k.hdr
|
||||
Intensity: 1
|
||||
Angle: 0
|
||||
AssetID: 3580169978473467053
|
||||
PhysicsLayers:
|
||||
- Name: Box
|
||||
CollidesWith:
|
||||
- Name: Default
|
||||
[]
|
||||
63
Editor/assets/scenes/demo.scene
Normal file
63
Editor/assets/scenes/demo.scene
Normal file
@ -0,0 +1,63 @@
|
||||
Scene: Scene Name
|
||||
Environment:
|
||||
AssetHandle: 1249421934001634765
|
||||
Light:
|
||||
Direction: [-0.314, -0.941, -0.209]
|
||||
Radiance: [0, 0, 0]
|
||||
Multiplier: 0
|
||||
Entities:
|
||||
- Entity: 18090260616187152572
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Directional Light
|
||||
TransformComponent:
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [0, 0, -2.7340124]
|
||||
Scale: [0.9999992, 0.9999992, 1]
|
||||
DirectionalLightComponent:
|
||||
Radiance: [1, 1, 1]
|
||||
CastShadows: true
|
||||
SoftShadows: true
|
||||
LightSize: 0.5
|
||||
- Entity: 18154648535203342017
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Cube
|
||||
TransformComponent:
|
||||
Position: [0, 6.282704, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
MeshComponent:
|
||||
AssetID: 133168951455480052
|
||||
- Entity: 386484027514287028
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Cube
|
||||
TransformComponent:
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [50, 1, 50]
|
||||
MeshComponent:
|
||||
AssetID: 133168951455480052
|
||||
- Entity: 6827989772960183355
|
||||
Parent: 0
|
||||
Children:
|
||||
[]
|
||||
TagComponent:
|
||||
Tag: Sky Light
|
||||
TransformComponent:
|
||||
Position: [0, 0, 0]
|
||||
Rotation: [0, 0, 0]
|
||||
Scale: [1, 1, 1]
|
||||
SkyLightComponent:
|
||||
EnvironmentMap: 1249421934001634765
|
||||
Intensity: 1
|
||||
Angle: 0
|
||||
PhysicsLayers:
|
||||
[]
|
||||
@ -1,45 +0,0 @@
|
||||
// Grid Shader
|
||||
|
||||
#type vertex
|
||||
#version 430
|
||||
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
layout(location = 1) in vec2 a_TexCoord;
|
||||
|
||||
uniform mat4 u_ViewProjection;
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
out vec2 v_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec4 position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
|
||||
gl_Position = position;
|
||||
|
||||
v_TexCoord = a_TexCoord;
|
||||
}
|
||||
|
||||
#type fragment
|
||||
#version 430
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
uniform float u_Scale;
|
||||
uniform float u_Res;
|
||||
|
||||
in vec2 v_TexCoord;
|
||||
|
||||
float grid(vec2 st, float res)
|
||||
{
|
||||
vec2 grid = fract(st);
|
||||
return step(res, grid.x) * step(res, grid.y);
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
float scale = u_Scale;
|
||||
float resolution = u_Res;
|
||||
|
||||
float x = grid(v_TexCoord * scale, resolution);
|
||||
color = vec4(vec3(0.2), 0.5) * (1.0 - x);
|
||||
}
|
||||
226
Editor/assets/shaders/InfiniteGrid.glsl
Normal file
226
Editor/assets/shaders/InfiniteGrid.glsl
Normal file
@ -0,0 +1,226 @@
|
||||
// Infinite Grid Shader
|
||||
// Based on "The Best Darn Grid Shader (Yet)" by Ben Golus
|
||||
|
||||
#type vertex
|
||||
#version 450 core
|
||||
|
||||
layout(location = 0) in vec3 a_Position;
|
||||
|
||||
// camera
|
||||
uniform mat4 u_View;
|
||||
uniform mat4 u_Projection;
|
||||
uniform vec3 u_CameraPosition;
|
||||
|
||||
out CameraData{
|
||||
mat4 View;
|
||||
mat4 Projection;
|
||||
vec3 CameraPosition;
|
||||
}CameraOutput;
|
||||
|
||||
out vec3 v_NearPoint;
|
||||
out vec3 v_FarPoint;
|
||||
|
||||
vec3 unprojectPoint(float x, float y, float z) {
|
||||
mat4 viewInv = inverse(u_View);
|
||||
mat4 projInv = inverse(u_Projection);
|
||||
vec4 unprojectedPoint = viewInv * projInv * vec4(x, y, z, 1.0);
|
||||
return unprojectedPoint.xyz / unprojectedPoint.w;
|
||||
}
|
||||
|
||||
void main() {
|
||||
v_NearPoint = unprojectPoint(a_Position.x, a_Position.y, 0.0);
|
||||
v_FarPoint = unprojectPoint(a_Position.x, a_Position.y, 1.0);
|
||||
|
||||
CameraOutput.View = u_View;
|
||||
CameraOutput.Projection = u_Projection;
|
||||
CameraOutput.CameraPosition = u_CameraPosition;
|
||||
|
||||
gl_Position = vec4(a_Position, 1.0);
|
||||
}
|
||||
|
||||
#type fragment
|
||||
#version 450 core
|
||||
|
||||
layout(location = 0) out vec4 o_Color;
|
||||
|
||||
in vec3 v_NearPoint;
|
||||
in vec3 v_FarPoint;
|
||||
|
||||
in CameraData{
|
||||
mat4 View;
|
||||
mat4 Projection;
|
||||
vec3 CameraPosition;
|
||||
}CameraInput;
|
||||
|
||||
// Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right)
|
||||
uniform int u_GridPlane = 0;
|
||||
uniform float u_GridScale = 1.0;
|
||||
uniform vec4 u_GridColorThin = vec4(0.5, 0.5, 0.5, 0.4);
|
||||
uniform vec4 u_GridColorThick = vec4(0.5, 0.5, 0.5, 0.6);
|
||||
uniform vec4 u_AxisColorX = vec4(0.9, 0.2, 0.2, 1.0);
|
||||
uniform vec4 u_AxisColorZ = vec4(0.2, 0.2, 0.9, 1.0);
|
||||
uniform float u_FadeDistance = 500.0;
|
||||
|
||||
float computeDepth(vec3 pos) {
|
||||
vec4 clipSpacePos = CameraInput.Projection * CameraInput.View * vec4(pos, 1.0);
|
||||
return clipSpacePos.z / clipSpacePos.w;
|
||||
}
|
||||
|
||||
// Get the plane normal based on grid plane type
|
||||
vec3 getPlaneNormal() {
|
||||
if (u_GridPlane == 1) return vec3(0.0, 0.0, 1.0); // XY plane, Z normal
|
||||
if (u_GridPlane == 2) return vec3(1.0, 0.0, 0.0); // YZ plane, X normal
|
||||
return vec3(0.0, 1.0, 0.0); // XZ plane, Y normal (default)
|
||||
}
|
||||
|
||||
// Get 2D coordinates on the plane
|
||||
vec2 getPlaneCoords(vec3 pos) {
|
||||
if (u_GridPlane == 1) return pos.xy; // XY plane
|
||||
if (u_GridPlane == 2) return pos.yz; // YZ plane
|
||||
return pos.xz; // XZ plane (default)
|
||||
}
|
||||
|
||||
// Get the component perpendicular to the plane (for axis drawing)
|
||||
vec2 getAxisCoords(vec3 pos) {
|
||||
// Returns the two coordinates used for drawing axis lines
|
||||
// First component -> first axis color, Second component -> second axis color
|
||||
if (u_GridPlane == 1) return vec2(pos.x, pos.y); // XY: X-axis and Y-axis
|
||||
if (u_GridPlane == 2) return vec2(pos.y, pos.z); // YZ: Y-axis and Z-axis
|
||||
return vec2(pos.x, pos.z); // XZ: X-axis and Z-axis
|
||||
}
|
||||
|
||||
// Calculate t for ray-plane intersection
|
||||
float rayPlaneIntersection(vec3 nearPoint, vec3 farPoint) {
|
||||
vec3 rayDir = farPoint - nearPoint;
|
||||
|
||||
if (u_GridPlane == 1) {
|
||||
// XY plane (z = 0)
|
||||
if (abs(rayDir.z) < 0.0001) return -1.0;
|
||||
return -nearPoint.z / rayDir.z;
|
||||
}
|
||||
if (u_GridPlane == 2) {
|
||||
// YZ plane (x = 0)
|
||||
if (abs(rayDir.x) < 0.0001) return -1.0;
|
||||
return -nearPoint.x / rayDir.x;
|
||||
}
|
||||
// XZ plane (y = 0) - default
|
||||
if (abs(rayDir.y) < 0.0001) return -1.0;
|
||||
return -nearPoint.y / rayDir.y;
|
||||
}
|
||||
|
||||
// Get view angle component for normal fade
|
||||
float getViewAngleComponent(vec3 viewDir) {
|
||||
if (u_GridPlane == 1) return abs(viewDir.z); // XY plane
|
||||
if (u_GridPlane == 2) return abs(viewDir.x); // YZ plane
|
||||
return abs(viewDir.y); // XZ plane
|
||||
}
|
||||
|
||||
// Pristine grid - single pixel line with proper AA
|
||||
float pristineGridLine(vec2 uv) {
|
||||
vec2 dudv = fwidth(uv);
|
||||
vec2 uvMod = fract(uv);
|
||||
vec2 uvDist = min(uvMod, 1.0 - uvMod);
|
||||
vec2 distInPixels = uvDist / dudv;
|
||||
vec2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels);
|
||||
float alpha = max(lineAlpha.x, lineAlpha.y);
|
||||
float density = max(dudv.x, dudv.y);
|
||||
float densityFade = 1.0 - smoothstep(0.5, 1.0, density);
|
||||
return alpha * densityFade;
|
||||
}
|
||||
|
||||
// Axis line - single pixel wide
|
||||
float axisLineAA(float coord, float dudv) {
|
||||
float distInPixels = abs(coord) / dudv;
|
||||
return 1.0 - smoothstep(0.0, 1.5, distInPixels);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
|
||||
float t = rayPlaneIntersection(v_NearPoint, v_FarPoint);
|
||||
|
||||
if (t < 0.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec3 fragPos3D = v_NearPoint + t * (v_FarPoint - v_NearPoint);
|
||||
float depth = computeDepth(fragPos3D);
|
||||
|
||||
if (depth > 1.0 || depth < -1.0) {
|
||||
discard;
|
||||
}
|
||||
|
||||
vec2 worldPos = getPlaneCoords(fragPos3D);
|
||||
|
||||
// === Fading ===
|
||||
|
||||
// Radial fade
|
||||
float dist = length(fragPos3D - CameraInput.CameraPosition);
|
||||
float radialFade = 1.0 - smoothstep(u_FadeDistance * 0.3, u_FadeDistance, dist);
|
||||
|
||||
// Normal fade (view angle)
|
||||
vec3 viewDir = normalize(fragPos3D - CameraInput.CameraPosition);
|
||||
float viewAngle = getViewAngleComponent(viewDir);
|
||||
float normalFade = smoothstep(0.0, 0.15, viewAngle);
|
||||
|
||||
float fadeFactor = radialFade * normalFade;
|
||||
|
||||
if (fadeFactor < 0.001) {
|
||||
discard;
|
||||
}
|
||||
|
||||
// === Grid calculation ===
|
||||
|
||||
vec2 gridCoord1 = worldPos / u_GridScale;
|
||||
vec2 gridCoord10 = worldPos / (u_GridScale * 10.0);
|
||||
|
||||
float grid1 = pristineGridLine(gridCoord1);
|
||||
float grid10 = pristineGridLine(gridCoord10);
|
||||
|
||||
// LOD blend
|
||||
vec2 deriv1 = fwidth(gridCoord1);
|
||||
float lodFactor = smoothstep(0.3, 0.6, max(deriv1.x, deriv1.y));
|
||||
|
||||
// Combine grids
|
||||
float gridIntensity = mix(max(grid1, grid10 * 0.7), grid10, lodFactor);
|
||||
|
||||
// Grid color
|
||||
vec3 gridColor = mix(u_GridColorThin.rgb, u_GridColorThick.rgb, lodFactor);
|
||||
float baseAlpha = mix(u_GridColorThin.a, u_GridColorThick.a, lodFactor);
|
||||
float gridAlpha = baseAlpha * gridIntensity * fadeFactor;
|
||||
|
||||
// === Axis lines ===
|
||||
|
||||
vec2 axisCoords = getAxisCoords(fragPos3D);
|
||||
vec2 worldDeriv = fwidth(worldPos);
|
||||
|
||||
// First axis (uses AxisColorX - typically red)
|
||||
float axis1Alpha = axisLineAA(axisCoords.y, worldDeriv.y) * fadeFactor;
|
||||
// Second axis (uses AxisColorZ - typically blue)
|
||||
float axis2Alpha = axisLineAA(axisCoords.x, worldDeriv.x) * fadeFactor;
|
||||
|
||||
// === Final composition ===
|
||||
|
||||
vec3 finalColor = gridColor;
|
||||
float finalAlpha = gridAlpha;
|
||||
|
||||
// Blend axis colors
|
||||
if (axis2Alpha > 0.001) {
|
||||
float blend = axis2Alpha * u_AxisColorZ.a;
|
||||
finalColor = mix(finalColor, u_AxisColorZ.rgb, blend);
|
||||
finalAlpha = max(finalAlpha, blend);
|
||||
}
|
||||
|
||||
if (axis1Alpha > 0.001) {
|
||||
float blend = axis1Alpha * u_AxisColorX.a;
|
||||
finalColor = mix(finalColor, u_AxisColorX.rgb, blend);
|
||||
finalAlpha = max(finalAlpha, blend);
|
||||
}
|
||||
|
||||
if (finalAlpha < 0.001) {
|
||||
discard;
|
||||
}
|
||||
|
||||
gl_FragDepth = depth * 0.5 + 0.5;
|
||||
o_Color = vec4(finalColor, finalAlpha);
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
// -----------------------------
|
||||
// -- Hazel Engine PBR shader --
|
||||
// -- Based on Hazel PBR shader --
|
||||
// -----------------------------
|
||||
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
|
||||
// Currently heavily updated.
|
||||
@ -21,53 +21,48 @@ layout(location = 4) in vec2 a_TexCoord;
|
||||
layout(location = 5) in ivec4 a_BoneIndices;
|
||||
layout(location = 6) in vec4 a_BoneWeights;
|
||||
|
||||
const int MAX_BONES = 100;
|
||||
uniform mat4 u_BoneTransforms[100];
|
||||
|
||||
uniform mat4 u_ViewProjectionMatrix;
|
||||
uniform mat4 u_ViewMatrix;
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
uniform mat4 u_LightMatrixCascade0;
|
||||
uniform mat4 u_LightMatrixCascade1;
|
||||
uniform mat4 u_LightMatrixCascade2;
|
||||
uniform mat4 u_LightMatrixCascade3;
|
||||
|
||||
const int MAX_BONES = 100;
|
||||
uniform mat4 u_BoneTransforms[100];
|
||||
uniform mat4 u_LightSpaceMatrix;
|
||||
|
||||
out VertexOutput
|
||||
{
|
||||
vec3 WorldPosition;
|
||||
vec3 Normal;
|
||||
vec2 TexCoord;
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec4 ShadowMapCoords[4];
|
||||
vec3 ViewPosition;
|
||||
vec3 WorldPosition;
|
||||
vec3 Normal;
|
||||
vec2 TexCoord;
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec3 ViewPosition;
|
||||
vec4 FragPosLightSpace;
|
||||
} vs_Output;
|
||||
|
||||
void main()
|
||||
{
|
||||
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
|
||||
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
|
||||
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
|
||||
|
||||
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
|
||||
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
|
||||
|
||||
vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0));
|
||||
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal;
|
||||
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
|
||||
vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
|
||||
vs_Output.WorldTransform = mat3(u_Transform);
|
||||
vs_Output.Binormal = a_Binormal;
|
||||
vs_Output.WorldPosition = vec3(u_Transform * boneTransform * vec4(a_Position, 1.0));
|
||||
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal;
|
||||
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
|
||||
vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
|
||||
vs_Output.WorldTransform = mat3(u_Transform);
|
||||
vs_Output.Binormal = a_Binormal;
|
||||
|
||||
vs_Output.ShadowMapCoords[0] = u_LightMatrixCascade0 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[1] = u_LightMatrixCascade1 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[2] = u_LightMatrixCascade2 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[3] = u_LightMatrixCascade3 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
|
||||
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0);
|
||||
|
||||
gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition;
|
||||
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
|
||||
|
||||
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
|
||||
}
|
||||
|
||||
#type fragment
|
||||
@ -77,27 +72,25 @@ const float PI = 3.141592;
|
||||
const float Epsilon = 0.00001;
|
||||
|
||||
const int LightCount = 1;
|
||||
|
||||
// Constant normal incidence Fresnel factor for all dielectrics.
|
||||
const vec3 Fdielectric = vec3(0.04);
|
||||
|
||||
struct DirectionalLight
|
||||
{
|
||||
vec3 Direction;
|
||||
vec3 Radiance;
|
||||
float Multiplier;
|
||||
vec3 Direction;
|
||||
vec3 Radiance;
|
||||
float Multiplier;
|
||||
};
|
||||
|
||||
in VertexOutput
|
||||
{
|
||||
vec3 WorldPosition;
|
||||
vec3 Normal;
|
||||
vec2 TexCoord;
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec4 ShadowMapCoords[4];
|
||||
vec3 ViewPosition;
|
||||
vec3 WorldPosition;
|
||||
vec3 Normal;
|
||||
vec2 TexCoord;
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec3 ViewPosition;
|
||||
vec4 FragPosLightSpace;
|
||||
} vs_Input;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
@ -106,533 +99,248 @@ layout(location = 1) out vec4 o_BloomColor;
|
||||
uniform DirectionalLight u_DirectionalLights;
|
||||
uniform vec3 u_CameraPosition;
|
||||
|
||||
// PBR texture inputs
|
||||
// PBR
|
||||
uniform sampler2D u_AlbedoTexture;
|
||||
uniform sampler2D u_NormalTexture;
|
||||
uniform sampler2D u_MetalnessTexture;
|
||||
uniform sampler2D u_RoughnessTexture;
|
||||
|
||||
// Environment maps
|
||||
// environment
|
||||
uniform samplerCube u_EnvRadianceTex;
|
||||
uniform samplerCube u_EnvIrradianceTex;
|
||||
|
||||
// BRDF LUT
|
||||
uniform sampler2D u_BRDFLUTTexture;
|
||||
|
||||
// PCSS
|
||||
uniform sampler2D u_ShadowMapTexture[4];
|
||||
uniform mat4 u_LightView;
|
||||
uniform bool u_ShowCascades;
|
||||
uniform bool u_SoftShadows;
|
||||
uniform float u_LightSize;
|
||||
uniform float u_MaxShadowDistance;
|
||||
uniform float u_ShadowFade;
|
||||
uniform bool u_CascadeFading;
|
||||
uniform float u_CascadeTransitionFade;
|
||||
|
||||
uniform vec4 u_CascadeSplits;
|
||||
|
||||
uniform float u_IBLContribution;
|
||||
|
||||
uniform float u_BloomThreshold;
|
||||
uniform float u_EnvMapRotation;
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
uniform vec3 u_AlbedoColor;
|
||||
// baseColor
|
||||
uniform vec3 u_AlbedoColor;
|
||||
uniform float u_Metalness;
|
||||
uniform float u_Roughness;
|
||||
|
||||
uniform float u_EnvMapRotation;
|
||||
|
||||
// Toggles
|
||||
// textureToggle
|
||||
uniform float u_AlbedoTexToggle;
|
||||
uniform float u_NormalTexToggle;
|
||||
uniform float u_MetalnessTexToggle;
|
||||
uniform float u_RoughnessTexToggle;
|
||||
|
||||
// shadow
|
||||
uniform sampler2D u_ShadowMap;
|
||||
uniform float u_ShadowBias;
|
||||
uniform float u_ShadowSoftness;
|
||||
uniform float u_ShadowIntensity;
|
||||
uniform int u_ShadowEnabled;
|
||||
|
||||
struct PBRParameters
|
||||
{
|
||||
vec3 Albedo;
|
||||
float Roughness;
|
||||
float Metalness;
|
||||
|
||||
vec3 Normal;
|
||||
vec3 View;
|
||||
float NdotV;
|
||||
vec3 Albedo;
|
||||
float Roughness;
|
||||
float Metalness;
|
||||
vec3 Normal;
|
||||
vec3 View;
|
||||
float NdotV;
|
||||
};
|
||||
|
||||
PBRParameters m_Params;
|
||||
|
||||
// GGX/Towbridge-Reitz normal distribution function.
|
||||
// Uses Disney's reparametrization of alpha = roughness^2
|
||||
// ---------- PBR param func ----------
|
||||
float ndfGGX(float cosLh, float roughness)
|
||||
{
|
||||
float alpha = roughness * roughness;
|
||||
float alphaSq = alpha * alpha;
|
||||
|
||||
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Single term for separable Schlick-GGX below.
|
||||
float gaSchlickG1(float cosTheta, float k)
|
||||
{
|
||||
return cosTheta / (cosTheta * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
|
||||
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
|
||||
float alpha = roughness * roughness;
|
||||
float alphaSq = alpha * alpha;
|
||||
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
||||
{
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r*r) / 8.0;
|
||||
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return nom / denom;
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r * r) / 8.0;
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
||||
{
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
// Shlick's approximation of the Fresnel factor.
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta)
|
||||
{
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
|
||||
{
|
||||
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
|
||||
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
|
||||
// so turning this on online will cause poor performance
|
||||
float RadicalInverse_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), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
|
||||
{
|
||||
float a = Roughness * Roughness;
|
||||
float Phi = 2 * PI * Xi.x;
|
||||
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
|
||||
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
|
||||
vec3 H;
|
||||
H.x = SinTheta * cos( Phi );
|
||||
H.y = SinTheta * sin( Phi );
|
||||
H.z = CosTheta;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
|
||||
vec3 TangentX = normalize( cross( UpVector, N ) );
|
||||
vec3 TangentY = cross( N, TangentX );
|
||||
// Tangent to world space
|
||||
return TangentX * H.x + TangentY * H.y + N * H.z;
|
||||
}
|
||||
|
||||
float TotalWeight = 0.0;
|
||||
|
||||
vec3 PrefilterEnvMap(float Roughness, vec3 R)
|
||||
{
|
||||
vec3 N = R;
|
||||
vec3 V = R;
|
||||
vec3 PrefilteredColor = vec3(0.0);
|
||||
int NumSamples = 1024;
|
||||
for(int i = 0; i < NumSamples; i++)
|
||||
{
|
||||
vec2 Xi = Hammersley(i, NumSamples);
|
||||
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
|
||||
vec3 L = 2 * dot(V, H) * H - V;
|
||||
float NoL = clamp(dot(N, L), 0.0, 1.0);
|
||||
if (NoL > 0)
|
||||
{
|
||||
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
|
||||
TotalWeight += NoL;
|
||||
}
|
||||
}
|
||||
return PrefilteredColor / TotalWeight;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
vec3 RotateVectorAboutY(float angle, vec3 vec)
|
||||
{
|
||||
angle = radians(angle);
|
||||
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
|
||||
vec3(0.0,1.0,0.0),
|
||||
vec3(-sin(angle),0.0,cos(angle))};
|
||||
return rotationMatrix * vec;
|
||||
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
// ---------- direction light ----------
|
||||
vec3 Lighting(vec3 F0)
|
||||
{
|
||||
vec3 result = vec3(0.0);
|
||||
for(int i = 0; i < LightCount; i++)
|
||||
{
|
||||
vec3 Li = u_DirectionalLights.Direction;
|
||||
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
|
||||
vec3 Lh = normalize(Li + m_Params.View);
|
||||
vec3 result = vec3(0.0);
|
||||
for(int i = 0; i < LightCount; i++)
|
||||
{
|
||||
vec3 Li = u_DirectionalLights.Direction;
|
||||
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
|
||||
vec3 Lh = normalize(Li + m_Params.View);
|
||||
|
||||
// Calculate angles between surface normal and various light vectors.
|
||||
float cosLi = max(0.0, dot(m_Params.Normal, Li));
|
||||
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
|
||||
float cosLi = max(0.0, dot(m_Params.Normal, Li));
|
||||
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
|
||||
|
||||
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
|
||||
float D = ndfGGX(cosLh, m_Params.Roughness);
|
||||
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
|
||||
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
|
||||
float D = ndfGGX(cosLh, m_Params.Roughness);
|
||||
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
|
||||
|
||||
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
|
||||
vec3 diffuseBRDF = kd * m_Params.Albedo;
|
||||
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
|
||||
vec3 diffuseBRDF = kd * m_Params.Albedo;
|
||||
|
||||
// Cook-Torrance
|
||||
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
|
||||
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
|
||||
|
||||
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
|
||||
}
|
||||
return result;
|
||||
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------- IBL ----------
|
||||
vec3 RotateVectorAboutY(float angle, vec3 vec)
|
||||
{
|
||||
angle = radians(angle);
|
||||
mat3 rotationMatrix = mat3(
|
||||
vec3(cos(angle), 0.0, sin(angle)),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(-sin(angle), 0.0, cos(angle))
|
||||
);
|
||||
return rotationMatrix * vec;
|
||||
}
|
||||
|
||||
vec3 IBL(vec3 F0, vec3 Lr)
|
||||
{
|
||||
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
|
||||
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
|
||||
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
|
||||
vec3 diffuseIBL = m_Params.Albedo * irradiance;
|
||||
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
|
||||
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
|
||||
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
|
||||
vec3 diffuseIBL = m_Params.Albedo * irradiance;
|
||||
|
||||
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
|
||||
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
|
||||
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
|
||||
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
|
||||
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
|
||||
vec3 specularIrradiance = textureLod(
|
||||
u_EnvRadianceTex,
|
||||
RotateVectorAboutY(u_EnvMapRotation, Lr),
|
||||
m_Params.Roughness * u_EnvRadianceTexLevels
|
||||
).rgb;
|
||||
|
||||
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
|
||||
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
|
||||
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
|
||||
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
|
||||
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
|
||||
|
||||
return kd * diffuseIBL + specularIBL;
|
||||
return kd * diffuseIBL + specularIBL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// PCSS
|
||||
/////////////////////////////////////////////
|
||||
// shadow
|
||||
|
||||
uint CascadeIndex = 0;
|
||||
float ShadowFade = 1.0;
|
||||
|
||||
float GetShadowBias()
|
||||
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
|
||||
{
|
||||
const float MINIMUM_SHADOW_BIAS = 0.002;
|
||||
float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS);
|
||||
return bias;
|
||||
// Perspective divide
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
|
||||
// Transform to [0,1] range
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
// If outside shadow map bounds, assume no shadow
|
||||
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||
return 0.0;
|
||||
|
||||
// Get closest depth value from light's perspective
|
||||
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
// Calculate bias based on surface angle
|
||||
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||
|
||||
// PCF (Percentage Closer Filtering) for soft shadows
|
||||
float shadow = 0.0;
|
||||
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||
int pcfRange = int(u_ShadowSoftness);
|
||||
int sampleCount = 0;
|
||||
|
||||
for(int x = -pcfRange; x <= pcfRange; ++x)
|
||||
{
|
||||
for(int y = -pcfRange; y <= pcfRange; ++y)
|
||||
{
|
||||
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||
sampleCount++;
|
||||
}
|
||||
}
|
||||
shadow /= float(sampleCount);
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords)
|
||||
|
||||
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
float z = texture(shadowMap, shadowCoords.xy).x;
|
||||
return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade;
|
||||
if (u_ShadowEnabled == 0) return 1.0;
|
||||
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||
projCoords.z > 1.0) return 1.0;
|
||||
|
||||
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
|
||||
|
||||
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
|
||||
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
|
||||
}
|
||||
|
||||
// Penumbra
|
||||
|
||||
// this search area estimation comes from the following article:
|
||||
// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf
|
||||
float SearchWidth(float uvLightSize, float receiverDistance)
|
||||
{
|
||||
const float NEAR = 0.1;
|
||||
return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z;
|
||||
}
|
||||
|
||||
float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj?
|
||||
float u_light_zFar = 10000.0;
|
||||
vec2 u_lightRadiusUV = vec2(0.05);
|
||||
|
||||
vec2 searchRegionRadiusUV(float zWorld)
|
||||
{
|
||||
return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld;
|
||||
}
|
||||
|
||||
const vec2 PoissonDistribution[64] = vec2[](
|
||||
vec2(-0.884081, 0.124488),
|
||||
vec2(-0.714377, 0.027940),
|
||||
vec2(-0.747945, 0.227922),
|
||||
vec2(-0.939609, 0.243634),
|
||||
vec2(-0.985465, 0.045534),
|
||||
vec2(-0.861367, -0.136222),
|
||||
vec2(-0.881934, 0.396908),
|
||||
vec2(-0.466938, 0.014526),
|
||||
vec2(-0.558207, 0.212662),
|
||||
vec2(-0.578447, -0.095822),
|
||||
vec2(-0.740266, -0.095631),
|
||||
vec2(-0.751681, 0.472604),
|
||||
vec2(-0.553147, -0.243177),
|
||||
vec2(-0.674762, -0.330730),
|
||||
vec2(-0.402765, -0.122087),
|
||||
vec2(-0.319776, -0.312166),
|
||||
vec2(-0.413923, -0.439757),
|
||||
vec2(-0.979153, -0.201245),
|
||||
vec2(-0.865579, -0.288695),
|
||||
vec2(-0.243704, -0.186378),
|
||||
vec2(-0.294920, -0.055748),
|
||||
vec2(-0.604452, -0.544251),
|
||||
vec2(-0.418056, -0.587679),
|
||||
vec2(-0.549156, -0.415877),
|
||||
vec2(-0.238080, -0.611761),
|
||||
vec2(-0.267004, -0.459702),
|
||||
vec2(-0.100006, -0.229116),
|
||||
vec2(-0.101928, -0.380382),
|
||||
vec2(-0.681467, -0.700773),
|
||||
vec2(-0.763488, -0.543386),
|
||||
vec2(-0.549030, -0.750749),
|
||||
vec2(-0.809045, -0.408738),
|
||||
vec2(-0.388134, -0.773448),
|
||||
vec2(-0.429392, -0.894892),
|
||||
vec2(-0.131597, 0.065058),
|
||||
vec2(-0.275002, 0.102922),
|
||||
vec2(-0.106117, -0.068327),
|
||||
vec2(-0.294586, -0.891515),
|
||||
vec2(-0.629418, 0.379387),
|
||||
vec2(-0.407257, 0.339748),
|
||||
vec2(0.071650, -0.384284),
|
||||
vec2(0.022018, -0.263793),
|
||||
vec2(0.003879, -0.136073),
|
||||
vec2(-0.137533, -0.767844),
|
||||
vec2(-0.050874, -0.906068),
|
||||
vec2(0.114133, -0.070053),
|
||||
vec2(0.163314, -0.217231),
|
||||
vec2(-0.100262, -0.587992),
|
||||
vec2(-0.004942, 0.125368),
|
||||
vec2(0.035302, -0.619310),
|
||||
vec2(0.195646, -0.459022),
|
||||
vec2(0.303969, -0.346362),
|
||||
vec2(-0.678118, 0.685099),
|
||||
vec2(-0.628418, 0.507978),
|
||||
vec2(-0.508473, 0.458753),
|
||||
vec2(0.032134, -0.782030),
|
||||
vec2(0.122595, 0.280353),
|
||||
vec2(-0.043643, 0.312119),
|
||||
vec2(0.132993, 0.085170),
|
||||
vec2(-0.192106, 0.285848),
|
||||
vec2(0.183621, -0.713242),
|
||||
vec2(0.265220, -0.596716),
|
||||
vec2(-0.009628, -0.483058),
|
||||
vec2(-0.018516, 0.435703)
|
||||
);
|
||||
|
||||
vec2 SamplePoisson(int index)
|
||||
{
|
||||
return PoissonDistribution[index % 64];
|
||||
}
|
||||
|
||||
float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
|
||||
int numBlockerSearchSamples = 64;
|
||||
int blockers = 0;
|
||||
float avgBlockerDistance = 0;
|
||||
|
||||
float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z;
|
||||
vec2 searchWidth = searchRegionRadiusUV(zEye);
|
||||
for (int i = 0; i < numBlockerSearchSamples; i++)
|
||||
{
|
||||
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r;
|
||||
if (z < (shadowCoords.z - bias))
|
||||
{
|
||||
blockers++;
|
||||
avgBlockerDistance += z;
|
||||
}
|
||||
}
|
||||
|
||||
if (blockers > 0)
|
||||
return avgBlockerDistance / float(blockers);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
{
|
||||
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
|
||||
if (blockerDistance == -1)
|
||||
return -1;
|
||||
|
||||
return (shadowCoords.z - blockerDistance) / blockerDistance;
|
||||
}
|
||||
|
||||
float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
int numPCFSamples = 64;
|
||||
float sum = 0;
|
||||
for (int i = 0; i < numPCFSamples; i++)
|
||||
{
|
||||
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r;
|
||||
sum += (z < (shadowCoords.z - bias)) ? 1 : 0;
|
||||
}
|
||||
return sum / numPCFSamples;
|
||||
}
|
||||
|
||||
float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
{
|
||||
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
|
||||
if (blockerDistance == -1)
|
||||
return 1;
|
||||
|
||||
float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance;
|
||||
|
||||
float NEAR = 0.01; // Should this value be tweakable?
|
||||
float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z;
|
||||
return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
void main()
|
||||
{
|
||||
// Standard PBR inputs
|
||||
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
|
||||
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
|
||||
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
|
||||
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
|
||||
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
|
||||
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
|
||||
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
|
||||
m_Params.Roughness = max(m_Params.Roughness, 0.05);
|
||||
|
||||
// Normals (either from vertex or map)
|
||||
m_Params.Normal = normalize(vs_Input.Normal);
|
||||
if (u_NormalTexToggle > 0.5)
|
||||
{
|
||||
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
|
||||
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
|
||||
}
|
||||
// normal
|
||||
m_Params.Normal = normalize(vs_Input.Normal);
|
||||
if (u_NormalTexToggle > 0.5)
|
||||
{
|
||||
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
|
||||
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
|
||||
}
|
||||
|
||||
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
|
||||
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
|
||||
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
|
||||
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
|
||||
|
||||
// Specular reflection vector
|
||||
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
|
||||
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
|
||||
|
||||
// Fresnel reflectance, metals use albedo
|
||||
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
|
||||
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
|
||||
|
||||
const uint SHADOW_MAP_CASCADE_COUNT = 4;
|
||||
for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++)
|
||||
{
|
||||
if(vs_Input.ViewPosition.z < u_CascadeSplits[i])
|
||||
CascadeIndex = i + 1;
|
||||
}
|
||||
float shadowFactor = 1.0;
|
||||
if (u_ShadowEnabled > 0.5) {
|
||||
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
|
||||
shadowFactor = 1.0 - shadow;
|
||||
}
|
||||
|
||||
float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3];
|
||||
float transitionDistance = u_ShadowFade;
|
||||
float distance = length(vs_Input.ViewPosition);
|
||||
ShadowFade = distance - (shadowDistance - transitionDistance);
|
||||
ShadowFade /= transitionDistance;
|
||||
ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0);
|
||||
// directional light with shadow
|
||||
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
|
||||
|
||||
bool fadeCascades = u_CascadeFading;
|
||||
float shadowAmount = 1.0;
|
||||
if (fadeCascades)
|
||||
{
|
||||
float cascadeTransitionFade = u_CascadeTransitionFade;
|
||||
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
|
||||
|
||||
float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
if (c0 > 0.0 && c0 < 1.0)
|
||||
{
|
||||
// Sample 0 & 1
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w);
|
||||
float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
|
||||
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
|
||||
color = vec4(lightContribution + iblContribution, 1.0);
|
||||
|
||||
shadowAmount = mix(shadowAmount0, shadowAmount1, c0);
|
||||
}
|
||||
else if (c1 > 0.0 && c1 < 1.0)
|
||||
{
|
||||
// Sample 1 & 2
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
|
||||
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
|
||||
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
|
||||
|
||||
shadowAmount = mix(shadowAmount1, shadowAmount2, c1);
|
||||
}
|
||||
else if (c2 > 0.0 && c2 < 1.0)
|
||||
{
|
||||
// Sample 2 & 3
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
|
||||
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w);
|
||||
float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords);
|
||||
|
||||
shadowAmount = mix(shadowAmount2, shadowAmount3, c2);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
|
||||
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
|
||||
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
|
||||
}
|
||||
|
||||
float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction);
|
||||
NdotL = smoothstep(0.0, 0.4, NdotL + 0.2);
|
||||
shadowAmount *= (NdotL * 1.0);
|
||||
|
||||
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
|
||||
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f);
|
||||
|
||||
color = vec4(lightContribution + iblContribution, 1.0);
|
||||
|
||||
// Bloom
|
||||
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
||||
o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
if (brightness > u_BloomThreshold)
|
||||
o_BloomColor = color;
|
||||
|
||||
if (u_ShowCascades)
|
||||
{
|
||||
switch(CascadeIndex)
|
||||
{
|
||||
case 0:
|
||||
color.rgb *= vec3(1.0f, 0.25f, 0.25f);
|
||||
break;
|
||||
case 1:
|
||||
color.rgb *= vec3(0.25f, 1.0f, 0.25f);
|
||||
break;
|
||||
case 2:
|
||||
color.rgb *= vec3(0.25f, 0.25f, 1.0f);
|
||||
break;
|
||||
case 3:
|
||||
color.rgb *= vec3(1.0f, 1.0f, 0.25f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Bloom
|
||||
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
||||
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
// -----------------------------
|
||||
// -- Hazel Engine PBR shader --
|
||||
// -- Based on Hazel PBR shader --
|
||||
// -----------------------------
|
||||
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
|
||||
// Currently heavily updated.
|
||||
@ -22,10 +22,7 @@ uniform mat4 u_ViewProjectionMatrix;
|
||||
uniform mat4 u_ViewMatrix;
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
uniform mat4 u_LightMatrixCascade0;
|
||||
uniform mat4 u_LightMatrixCascade1;
|
||||
uniform mat4 u_LightMatrixCascade2;
|
||||
uniform mat4 u_LightMatrixCascade3;
|
||||
uniform mat4 u_LightSpaceMatrix;
|
||||
|
||||
out VertexOutput
|
||||
{
|
||||
@ -35,8 +32,8 @@ out VertexOutput
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec4 ShadowMapCoords[4];
|
||||
vec3 ViewPosition;
|
||||
vec4 FragPosLightSpace;
|
||||
} vs_Output;
|
||||
|
||||
void main()
|
||||
@ -48,10 +45,8 @@ void main()
|
||||
vs_Output.WorldTransform = mat3(u_Transform);
|
||||
vs_Output.Binormal = a_Binormal;
|
||||
|
||||
vs_Output.ShadowMapCoords[0] = u_LightMatrixCascade0 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[1] = u_LightMatrixCascade1 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[2] = u_LightMatrixCascade2 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.ShadowMapCoords[3] = u_LightMatrixCascade3 * vec4(vs_Output.WorldPosition, 1.0);
|
||||
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0);
|
||||
|
||||
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
|
||||
|
||||
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
|
||||
@ -64,8 +59,6 @@ const float PI = 3.141592;
|
||||
const float Epsilon = 0.00001;
|
||||
|
||||
const int LightCount = 1;
|
||||
|
||||
// Constant normal incidence Fresnel factor for all dielectrics.
|
||||
const vec3 Fdielectric = vec3(0.04);
|
||||
|
||||
struct DirectionalLight
|
||||
@ -83,8 +76,8 @@ in VertexOutput
|
||||
mat3 WorldNormals;
|
||||
mat3 WorldTransform;
|
||||
vec3 Binormal;
|
||||
vec4 ShadowMapCoords[4];
|
||||
vec3 ViewPosition;
|
||||
vec4 FragPosLightSpace;
|
||||
} vs_Input;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
@ -93,56 +86,46 @@ layout(location = 1) out vec4 o_BloomColor;
|
||||
uniform DirectionalLight u_DirectionalLights;
|
||||
uniform vec3 u_CameraPosition;
|
||||
|
||||
// PBR texture inputs
|
||||
// PBR
|
||||
uniform sampler2D u_AlbedoTexture;
|
||||
uniform sampler2D u_NormalTexture;
|
||||
uniform sampler2D u_MetalnessTexture;
|
||||
uniform sampler2D u_RoughnessTexture;
|
||||
|
||||
// Environment maps
|
||||
// environment
|
||||
uniform samplerCube u_EnvRadianceTex;
|
||||
uniform samplerCube u_EnvIrradianceTex;
|
||||
|
||||
// BRDF LUT
|
||||
uniform sampler2D u_BRDFLUTTexture;
|
||||
|
||||
// PCSS
|
||||
uniform sampler2D u_ShadowMapTexture[4];
|
||||
uniform mat4 u_LightView;
|
||||
uniform bool u_ShowCascades;
|
||||
uniform bool u_SoftShadows;
|
||||
uniform float u_LightSize;
|
||||
uniform float u_MaxShadowDistance;
|
||||
uniform float u_ShadowFade;
|
||||
uniform bool u_CascadeFading;
|
||||
uniform float u_CascadeTransitionFade;
|
||||
|
||||
uniform vec4 u_CascadeSplits;
|
||||
|
||||
uniform float u_IBLContribution;
|
||||
|
||||
uniform float u_BloomThreshold;
|
||||
uniform float u_EnvMapRotation;
|
||||
|
||||
////////////////////////////////////////
|
||||
|
||||
uniform vec3 u_AlbedoColor;
|
||||
// baseColor
|
||||
uniform vec3 u_AlbedoColor;
|
||||
uniform float u_Metalness;
|
||||
uniform float u_Roughness;
|
||||
|
||||
uniform float u_EnvMapRotation;
|
||||
|
||||
// Toggles
|
||||
// textureToggle
|
||||
uniform float u_AlbedoTexToggle;
|
||||
uniform float u_NormalTexToggle;
|
||||
uniform float u_MetalnessTexToggle;
|
||||
uniform float u_RoughnessTexToggle;
|
||||
|
||||
// shadow
|
||||
uniform sampler2D u_ShadowMap;
|
||||
uniform float u_ShadowBias;
|
||||
uniform float u_ShadowSoftness;
|
||||
uniform float u_ShadowIntensity;
|
||||
uniform int u_ShadowEnabled;
|
||||
|
||||
struct PBRParameters
|
||||
{
|
||||
vec3 Albedo;
|
||||
float Roughness;
|
||||
float Metalness;
|
||||
|
||||
vec3 Normal;
|
||||
vec3 View;
|
||||
float NdotV;
|
||||
@ -150,39 +133,21 @@ struct PBRParameters
|
||||
|
||||
PBRParameters m_Params;
|
||||
|
||||
// GGX/Towbridge-Reitz normal distribution function.
|
||||
// Uses Disney's reparametrization of alpha = roughness^2
|
||||
// ---------- PBR param func ----------
|
||||
float ndfGGX(float cosLh, float roughness)
|
||||
{
|
||||
float alpha = roughness * roughness;
|
||||
float alphaSq = alpha * alpha;
|
||||
|
||||
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
|
||||
return alphaSq / (PI * denom * denom);
|
||||
}
|
||||
|
||||
// Single term for separable Schlick-GGX below.
|
||||
float gaSchlickG1(float cosTheta, float k)
|
||||
{
|
||||
return cosTheta / (cosTheta * (1.0 - k) + k);
|
||||
}
|
||||
|
||||
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
|
||||
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
|
||||
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
||||
{
|
||||
float r = (roughness + 1.0);
|
||||
float k = (r*r) / 8.0;
|
||||
|
||||
float nom = NdotV;
|
||||
float k = (r * r) / 8.0;
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
@ -192,11 +157,9 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
// Shlick's approximation of the Fresnel factor.
|
||||
vec3 fresnelSchlick(vec3 F0, float cosTheta)
|
||||
{
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
@ -207,76 +170,7 @@ vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
|
||||
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
|
||||
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
|
||||
// so turning this on online will cause poor performance
|
||||
float RadicalInverse_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), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
|
||||
{
|
||||
float a = Roughness * Roughness;
|
||||
float Phi = 2 * PI * Xi.x;
|
||||
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
|
||||
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
|
||||
vec3 H;
|
||||
H.x = SinTheta * cos( Phi );
|
||||
H.y = SinTheta * sin( Phi );
|
||||
H.z = CosTheta;
|
||||
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
|
||||
vec3 TangentX = normalize( cross( UpVector, N ) );
|
||||
vec3 TangentY = cross( N, TangentX );
|
||||
// Tangent to world space
|
||||
return TangentX * H.x + TangentY * H.y + N * H.z;
|
||||
}
|
||||
|
||||
float TotalWeight = 0.0;
|
||||
|
||||
vec3 PrefilterEnvMap(float Roughness, vec3 R)
|
||||
{
|
||||
vec3 N = R;
|
||||
vec3 V = R;
|
||||
vec3 PrefilteredColor = vec3(0.0);
|
||||
int NumSamples = 1024;
|
||||
for(int i = 0; i < NumSamples; i++)
|
||||
{
|
||||
vec2 Xi = Hammersley(i, NumSamples);
|
||||
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
|
||||
vec3 L = 2 * dot(V, H) * H - V;
|
||||
float NoL = clamp(dot(N, L), 0.0, 1.0);
|
||||
if (NoL > 0)
|
||||
{
|
||||
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
|
||||
TotalWeight += NoL;
|
||||
}
|
||||
}
|
||||
return PrefilteredColor / TotalWeight;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------
|
||||
|
||||
vec3 RotateVectorAboutY(float angle, vec3 vec)
|
||||
{
|
||||
angle = radians(angle);
|
||||
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
|
||||
vec3(0.0,1.0,0.0),
|
||||
vec3(-sin(angle),0.0,cos(angle))};
|
||||
return rotationMatrix * vec;
|
||||
}
|
||||
|
||||
// ---------- direction light ----------
|
||||
vec3 Lighting(vec3 F0)
|
||||
{
|
||||
vec3 result = vec3(0.0);
|
||||
@ -286,18 +180,16 @@ vec3 Lighting(vec3 F0)
|
||||
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Multiplier;
|
||||
vec3 Lh = normalize(Li + m_Params.View);
|
||||
|
||||
// Calculate angles between surface normal and various light vectors.
|
||||
float cosLi = max(0.0, dot(m_Params.Normal, Li));
|
||||
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
|
||||
|
||||
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
|
||||
float D = ndfGGX(cosLh, m_Params.Roughness);
|
||||
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
|
||||
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
|
||||
|
||||
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
|
||||
vec3 diffuseBRDF = kd * m_Params.Albedo;
|
||||
|
||||
// Cook-Torrance
|
||||
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
|
||||
|
||||
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
|
||||
@ -305,6 +197,18 @@ vec3 Lighting(vec3 F0)
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------- IBL ----------
|
||||
vec3 RotateVectorAboutY(float angle, vec3 vec)
|
||||
{
|
||||
angle = radians(angle);
|
||||
mat3 rotationMatrix = mat3(
|
||||
vec3(cos(angle), 0.0, sin(angle)),
|
||||
vec3(0.0, 1.0, 0.0),
|
||||
vec3(-sin(angle), 0.0, cos(angle))
|
||||
);
|
||||
return rotationMatrix * vec;
|
||||
}
|
||||
|
||||
vec3 IBL(vec3 F0, vec3 Lr)
|
||||
{
|
||||
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
|
||||
@ -313,201 +217,89 @@ vec3 IBL(vec3 F0, vec3 Lr)
|
||||
vec3 diffuseIBL = m_Params.Albedo * irradiance;
|
||||
|
||||
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
|
||||
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
|
||||
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
|
||||
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
|
||||
vec3 specularIrradiance = textureLod(
|
||||
u_EnvRadianceTex,
|
||||
RotateVectorAboutY(u_EnvMapRotation, Lr),
|
||||
m_Params.Roughness * u_EnvRadianceTexLevels
|
||||
).rgb;
|
||||
|
||||
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
|
||||
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
|
||||
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
|
||||
|
||||
return kd * diffuseIBL + specularIBL;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// PCSS
|
||||
/////////////////////////////////////////////
|
||||
// shadow
|
||||
|
||||
uint CascadeIndex = 0;
|
||||
float ShadowFade = 1.0;
|
||||
|
||||
float GetShadowBias()
|
||||
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
|
||||
{
|
||||
const float MINIMUM_SHADOW_BIAS = 0.002;
|
||||
float bias = max(MINIMUM_SHADOW_BIAS * (1.0 - dot(m_Params.Normal, u_DirectionalLights.Direction)), MINIMUM_SHADOW_BIAS);
|
||||
return bias;
|
||||
}
|
||||
// Perspective divide
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
|
||||
float HardShadows_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
float z = texture(shadowMap, shadowCoords.xy).x;
|
||||
return 1.0 - step(z + bias, shadowCoords.z) * ShadowFade;
|
||||
}
|
||||
// Transform to [0,1] range
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
// Penumbra
|
||||
// If outside shadow map bounds, assume no shadow
|
||||
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
|
||||
return 0.0;
|
||||
|
||||
// this search area estimation comes from the following article:
|
||||
// http://developer.download.nvidia.com/whitepapers/2008/PCSS_DirectionalLight_Integration.pdf
|
||||
float SearchWidth(float uvLightSize, float receiverDistance)
|
||||
{
|
||||
const float NEAR = 0.1;
|
||||
return uvLightSize * (receiverDistance - NEAR) / u_CameraPosition.z;
|
||||
}
|
||||
// Get closest depth value from light's perspective
|
||||
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
float u_light_zNear = 0.0; // 0.01 gives artifacts? maybe because of ortho proj?
|
||||
float u_light_zFar = 10000.0;
|
||||
vec2 u_lightRadiusUV = vec2(0.05);
|
||||
// Calculate bias based on surface angle
|
||||
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
|
||||
|
||||
vec2 searchRegionRadiusUV(float zWorld)
|
||||
{
|
||||
return u_lightRadiusUV * (zWorld - u_light_zNear) / zWorld;
|
||||
}
|
||||
// PCF (Percentage Closer Filtering) for soft shadows
|
||||
float shadow = 0.0;
|
||||
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
|
||||
int pcfRange = int(u_ShadowSoftness);
|
||||
int sampleCount = 0;
|
||||
|
||||
const vec2 PoissonDistribution[64] = vec2[](
|
||||
vec2(-0.884081, 0.124488),
|
||||
vec2(-0.714377, 0.027940),
|
||||
vec2(-0.747945, 0.227922),
|
||||
vec2(-0.939609, 0.243634),
|
||||
vec2(-0.985465, 0.045534),
|
||||
vec2(-0.861367, -0.136222),
|
||||
vec2(-0.881934, 0.396908),
|
||||
vec2(-0.466938, 0.014526),
|
||||
vec2(-0.558207, 0.212662),
|
||||
vec2(-0.578447, -0.095822),
|
||||
vec2(-0.740266, -0.095631),
|
||||
vec2(-0.751681, 0.472604),
|
||||
vec2(-0.553147, -0.243177),
|
||||
vec2(-0.674762, -0.330730),
|
||||
vec2(-0.402765, -0.122087),
|
||||
vec2(-0.319776, -0.312166),
|
||||
vec2(-0.413923, -0.439757),
|
||||
vec2(-0.979153, -0.201245),
|
||||
vec2(-0.865579, -0.288695),
|
||||
vec2(-0.243704, -0.186378),
|
||||
vec2(-0.294920, -0.055748),
|
||||
vec2(-0.604452, -0.544251),
|
||||
vec2(-0.418056, -0.587679),
|
||||
vec2(-0.549156, -0.415877),
|
||||
vec2(-0.238080, -0.611761),
|
||||
vec2(-0.267004, -0.459702),
|
||||
vec2(-0.100006, -0.229116),
|
||||
vec2(-0.101928, -0.380382),
|
||||
vec2(-0.681467, -0.700773),
|
||||
vec2(-0.763488, -0.543386),
|
||||
vec2(-0.549030, -0.750749),
|
||||
vec2(-0.809045, -0.408738),
|
||||
vec2(-0.388134, -0.773448),
|
||||
vec2(-0.429392, -0.894892),
|
||||
vec2(-0.131597, 0.065058),
|
||||
vec2(-0.275002, 0.102922),
|
||||
vec2(-0.106117, -0.068327),
|
||||
vec2(-0.294586, -0.891515),
|
||||
vec2(-0.629418, 0.379387),
|
||||
vec2(-0.407257, 0.339748),
|
||||
vec2(0.071650, -0.384284),
|
||||
vec2(0.022018, -0.263793),
|
||||
vec2(0.003879, -0.136073),
|
||||
vec2(-0.137533, -0.767844),
|
||||
vec2(-0.050874, -0.906068),
|
||||
vec2(0.114133, -0.070053),
|
||||
vec2(0.163314, -0.217231),
|
||||
vec2(-0.100262, -0.587992),
|
||||
vec2(-0.004942, 0.125368),
|
||||
vec2(0.035302, -0.619310),
|
||||
vec2(0.195646, -0.459022),
|
||||
vec2(0.303969, -0.346362),
|
||||
vec2(-0.678118, 0.685099),
|
||||
vec2(-0.628418, 0.507978),
|
||||
vec2(-0.508473, 0.458753),
|
||||
vec2(0.032134, -0.782030),
|
||||
vec2(0.122595, 0.280353),
|
||||
vec2(-0.043643, 0.312119),
|
||||
vec2(0.132993, 0.085170),
|
||||
vec2(-0.192106, 0.285848),
|
||||
vec2(0.183621, -0.713242),
|
||||
vec2(0.265220, -0.596716),
|
||||
vec2(-0.009628, -0.483058),
|
||||
vec2(-0.018516, 0.435703)
|
||||
);
|
||||
|
||||
vec2 SamplePoisson(int index)
|
||||
{
|
||||
return PoissonDistribution[index % 64];
|
||||
}
|
||||
|
||||
float FindBlockerDistance_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
|
||||
int numBlockerSearchSamples = 64;
|
||||
int blockers = 0;
|
||||
float avgBlockerDistance = 0;
|
||||
|
||||
float zEye = -(u_LightView * vec4(vs_Input.WorldPosition, 1.0)).z;
|
||||
vec2 searchWidth = searchRegionRadiusUV(zEye);
|
||||
for (int i = 0; i < numBlockerSearchSamples; i++)
|
||||
for(int x = -pcfRange; x <= pcfRange; ++x)
|
||||
{
|
||||
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * searchWidth).r;
|
||||
if (z < (shadowCoords.z - bias))
|
||||
for(int y = -pcfRange; y <= pcfRange; ++y)
|
||||
{
|
||||
blockers++;
|
||||
avgBlockerDistance += z;
|
||||
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
|
||||
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
|
||||
sampleCount++;
|
||||
}
|
||||
}
|
||||
shadow /= float(sampleCount);
|
||||
|
||||
if (blockers > 0)
|
||||
return avgBlockerDistance / float(blockers);
|
||||
|
||||
return -1;
|
||||
return shadow;
|
||||
}
|
||||
|
||||
float PenumbraWidth(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
|
||||
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
|
||||
{
|
||||
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
|
||||
if (blockerDistance == -1)
|
||||
return -1;
|
||||
if (u_ShadowEnabled == 0) return 1.0;
|
||||
|
||||
return (shadowCoords.z - blockerDistance) / blockerDistance;
|
||||
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
|
||||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
|
||||
projCoords.z > 1.0) return 1.0;
|
||||
|
||||
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
|
||||
|
||||
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
|
||||
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
|
||||
}
|
||||
|
||||
float PCF_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvRadius)
|
||||
{
|
||||
float bias = GetShadowBias();
|
||||
int numPCFSamples = 64;
|
||||
float sum = 0;
|
||||
for (int i = 0; i < numPCFSamples; i++)
|
||||
{
|
||||
float z = texture(shadowMap, shadowCoords.xy + SamplePoisson(i) * uvRadius).r;
|
||||
sum += (z < (shadowCoords.z - bias)) ? 1 : 0;
|
||||
}
|
||||
return sum / numPCFSamples;
|
||||
}
|
||||
|
||||
float PCSS_DirectionalLight(sampler2D shadowMap, vec3 shadowCoords, float uvLightSize)
|
||||
{
|
||||
float blockerDistance = FindBlockerDistance_DirectionalLight(shadowMap, shadowCoords, uvLightSize);
|
||||
if (blockerDistance == -1)
|
||||
return 1;
|
||||
|
||||
float penumbraWidth = (shadowCoords.z - blockerDistance) / blockerDistance;
|
||||
|
||||
float NEAR = 0.01; // Should this value be tweakable?
|
||||
float uvRadius = penumbraWidth * uvLightSize * NEAR / shadowCoords.z;
|
||||
return 1.0 - PCF_DirectionalLight(shadowMap, shadowCoords, uvRadius) * ShadowFade;
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
|
||||
void main()
|
||||
{
|
||||
// Standard PBR inputs
|
||||
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
|
||||
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
|
||||
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
|
||||
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
|
||||
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
|
||||
m_Params.Roughness = max(m_Params.Roughness, 0.05);
|
||||
|
||||
// Normals (either from vertex or map)
|
||||
// normal
|
||||
m_Params.Normal = normalize(vs_Input.Normal);
|
||||
if (u_NormalTexToggle > 0.5)
|
||||
{
|
||||
@ -518,108 +310,24 @@ void main()
|
||||
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
|
||||
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
|
||||
|
||||
// Specular reflection vector
|
||||
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
|
||||
|
||||
// Fresnel reflectance, metals use albedo
|
||||
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
|
||||
|
||||
const uint SHADOW_MAP_CASCADE_COUNT = 4;
|
||||
for(uint i = 0; i < SHADOW_MAP_CASCADE_COUNT - 1; i++)
|
||||
{
|
||||
if(vs_Input.ViewPosition.z < u_CascadeSplits[i])
|
||||
CascadeIndex = i + 1;
|
||||
float shadowFactor = 1.0;
|
||||
if (u_ShadowEnabled > 0.5) {
|
||||
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
|
||||
shadowFactor = 1.0 - shadow;
|
||||
}
|
||||
|
||||
float shadowDistance = u_MaxShadowDistance;//u_CascadeSplits[3];
|
||||
float transitionDistance = u_ShadowFade;
|
||||
float distance = length(vs_Input.ViewPosition);
|
||||
ShadowFade = distance - (shadowDistance - transitionDistance);
|
||||
ShadowFade /= transitionDistance;
|
||||
ShadowFade = clamp(1.0 - ShadowFade, 0.0, 1.0);
|
||||
|
||||
bool fadeCascades = u_CascadeFading;
|
||||
float shadowAmount = 1.0;
|
||||
if (fadeCascades)
|
||||
{
|
||||
float cascadeTransitionFade = u_CascadeTransitionFade;
|
||||
|
||||
float c0 = smoothstep(u_CascadeSplits[0] + cascadeTransitionFade * 0.5f, u_CascadeSplits[0] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
float c1 = smoothstep(u_CascadeSplits[1] + cascadeTransitionFade * 0.5f, u_CascadeSplits[1] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
float c2 = smoothstep(u_CascadeSplits[2] + cascadeTransitionFade * 0.5f, u_CascadeSplits[2] - cascadeTransitionFade * 0.5f, vs_Input.ViewPosition.z);
|
||||
if (c0 > 0.0 && c0 < 1.0)
|
||||
{
|
||||
// Sample 0 & 1
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[0].xyz / vs_Input.ShadowMapCoords[0].w);
|
||||
float shadowAmount0 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[0], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
|
||||
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
|
||||
|
||||
shadowAmount = mix(shadowAmount0, shadowAmount1, c0);
|
||||
}
|
||||
else if (c1 > 0.0 && c1 < 1.0)
|
||||
{
|
||||
// Sample 1 & 2
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[1].xyz / vs_Input.ShadowMapCoords[1].w);
|
||||
float shadowAmount1 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[1], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
|
||||
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
|
||||
|
||||
shadowAmount = mix(shadowAmount1, shadowAmount2, c1);
|
||||
}
|
||||
else if (c2 > 0.0 && c2 < 1.0)
|
||||
{
|
||||
// Sample 2 & 3
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[2].xyz / vs_Input.ShadowMapCoords[2].w);
|
||||
float shadowAmount2 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[2], shadowMapCoords);
|
||||
shadowMapCoords = (vs_Input.ShadowMapCoords[3].xyz / vs_Input.ShadowMapCoords[3].w);
|
||||
float shadowAmount3 = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[3], shadowMapCoords);
|
||||
|
||||
shadowAmount = mix(shadowAmount2, shadowAmount3, c2);
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
|
||||
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
vec3 shadowMapCoords = (vs_Input.ShadowMapCoords[CascadeIndex].xyz / vs_Input.ShadowMapCoords[CascadeIndex].w);
|
||||
shadowAmount = u_SoftShadows ? PCSS_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords, u_LightSize) : HardShadows_DirectionalLight(u_ShadowMapTexture[CascadeIndex], shadowMapCoords);
|
||||
}
|
||||
|
||||
float NdotL = dot(m_Params.Normal, u_DirectionalLights.Direction);
|
||||
NdotL = smoothstep(0.0, 0.4, NdotL + 0.2);
|
||||
shadowAmount *= (NdotL * 1.0);
|
||||
// directional light with with shadow
|
||||
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
|
||||
|
||||
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
|
||||
vec3 lightContribution = u_DirectionalLights.Multiplier > 0.0f ? (Lighting(F0) * shadowAmount) : vec3(0.0f);
|
||||
|
||||
color = vec4(lightContribution + iblContribution, 1.0);
|
||||
|
||||
// Bloom
|
||||
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
|
||||
o_BloomColor = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
if (brightness > u_BloomThreshold)
|
||||
o_BloomColor = color;
|
||||
|
||||
if (u_ShowCascades)
|
||||
{
|
||||
switch(CascadeIndex)
|
||||
{
|
||||
case 0:
|
||||
color.rgb *= vec3(1.0f, 0.25f, 0.25f);
|
||||
break;
|
||||
case 1:
|
||||
color.rgb *= vec3(0.25f, 1.0f, 0.25f);
|
||||
break;
|
||||
case 2:
|
||||
color.rgb *= vec3(0.25f, 0.25f, 1.0f);
|
||||
break;
|
||||
case 3:
|
||||
color.rgb *= vec3(1.0f, 1.0f, 0.25f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@ -222,6 +222,27 @@ namespace Prism::UI {
|
||||
return modified;
|
||||
}
|
||||
|
||||
static bool PropertyColor(const char* label, glm::vec4& value)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
ImGui::Text("%s", label);
|
||||
ImGui::NextColumn();
|
||||
ImGui::PushItemWidth(-1);
|
||||
|
||||
s_IDBuffer[0] = '#';
|
||||
s_IDBuffer[1] = '#';
|
||||
memset(s_IDBuffer + 2, 0, 14);
|
||||
snprintf(s_IDBuffer + 2, 14, "%x", s_Counter++);
|
||||
if (ImGui::ColorEdit4(s_IDBuffer, glm::value_ptr(value)))
|
||||
modified = true;
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::NextColumn();
|
||||
|
||||
return modified;
|
||||
}
|
||||
|
||||
static bool Property(const char* label, glm::vec3& value, const float delta = 0.1f)
|
||||
{
|
||||
bool modified = false;
|
||||
|
||||
@ -53,7 +53,7 @@ namespace Prism
|
||||
uint32_t Height = 720;
|
||||
glm::vec4 ClearColor;
|
||||
FramebufferAttachmentSpecification Attachments;
|
||||
uint32_t Samples = 1; // multisampling
|
||||
int Samples = 1; // multisampling
|
||||
|
||||
// TODO: Temp, needs scale
|
||||
bool NoResize = false;
|
||||
|
||||
@ -158,7 +158,7 @@ namespace Prism
|
||||
void Set(const std::string& name, const T& value)
|
||||
{
|
||||
const auto decl = m_Material->FindUniformDeclaration(name);
|
||||
if (!decl) PM_CORE_ERROR("Could not find uniform with name {0}", name);
|
||||
if (!decl) PM_CORE_ERROR("Could not find uniform with name '{0}'", name);
|
||||
const auto& buffer = GetUniformBufferTarget(decl);
|
||||
buffer.Write(&value, decl->GetSize(), decl->GetOffset());
|
||||
|
||||
|
||||
@ -42,24 +42,26 @@ namespace Prism
|
||||
Ref<RenderPass> CompositePass;
|
||||
Ref<RenderPass> BloomBlurPass[2];
|
||||
Ref<RenderPass> BloomBlendPass;
|
||||
Ref<RenderPass> LuminancePass;
|
||||
|
||||
// Auto exposure
|
||||
struct AutoExposure
|
||||
struct AutoExposureData
|
||||
{
|
||||
Ref<RenderPass> LuminancePass;
|
||||
|
||||
float CurrentExposure = 1.0f;
|
||||
bool EnableAutoExposure = true;
|
||||
float Key = 0.18f; // middle gray
|
||||
float AdaptationSpeed = 1.0f; // stops per second
|
||||
float AdaptationSpeed = 5.0f; // stops per second
|
||||
|
||||
Timer ExposureTimer;
|
||||
float MaxExposure = 5.0f;
|
||||
}AutoExposure;
|
||||
}AutoExposureData;
|
||||
|
||||
Ref<Shader> ShadowMapShader, ShadowMapAnimShader;
|
||||
Ref<RenderPass> ShadowMapRenderPass[4];
|
||||
Ref<RenderPass> ShadowMapRenderPass;
|
||||
float ShadowMapSize = 20.0f;
|
||||
float LightDistance = 0.1f;
|
||||
glm::mat4 LightMatrices[4];
|
||||
glm::mat4 LightMatrices;
|
||||
glm::mat4 LightViewMatrix;
|
||||
float CascadeSplitLambda = 0.91f;
|
||||
glm::vec4 CascadeSplits;
|
||||
@ -79,6 +81,11 @@ namespace Prism
|
||||
|
||||
RendererID ShadowMapSampler;
|
||||
|
||||
bool ShadowEnabled = true;
|
||||
float ShadowBias = 0.001f;
|
||||
float ShadowIntensity = 0.0f;
|
||||
int ShadowSoftness = 0;
|
||||
|
||||
struct DrawCommand
|
||||
{
|
||||
Ref<Mesh> mesh;
|
||||
@ -90,11 +97,23 @@ namespace Prism
|
||||
std::vector<DrawCommand> ColliderDrawList;
|
||||
std::vector<DrawCommand> ShadowPassDrawList;
|
||||
|
||||
// Grid
|
||||
Ref<MaterialInstance> GridMaterial;
|
||||
Ref<MaterialInstance> OutlineMaterial, OutlineAnimMaterial;
|
||||
Ref<MaterialInstance> ColliderMaterial;
|
||||
|
||||
// Grid
|
||||
struct GridData
|
||||
{
|
||||
Ref<MaterialInstance> GridMaterial;
|
||||
|
||||
int GridPlane = 0;
|
||||
float GridScale = 1.0f;
|
||||
glm::vec4 GridColorThin = glm::vec4(0.5f, 0.5f, 0.5f, 0.4f);
|
||||
glm::vec4 GridColorThick = glm::vec4(0.5f, 0.5f, 0.5f, 0.6f);
|
||||
glm::vec4 AxisColorX = glm::vec4(0.9f, 0.2f, 0.2f, 1.0f);
|
||||
glm::vec4 AxisColorZ = glm::vec4(0.2f, 0.2f, 0.9f, 1.0f);
|
||||
float FadeDistance = 500.0f;
|
||||
}GridData;
|
||||
|
||||
SceneRendererOptions Options;
|
||||
};
|
||||
|
||||
@ -114,92 +133,6 @@ namespace Prism
|
||||
static SceneRendererData s_Data;
|
||||
static SceneRendererStats s_Stats;
|
||||
|
||||
static void ComputeAutoExposure()
|
||||
{
|
||||
if (!s_Data.AutoExposure.EnableAutoExposure)
|
||||
return;
|
||||
|
||||
auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer;
|
||||
auto dstFB = s_Data.LuminancePass->GetSpecification().TargetFramebuffer;
|
||||
if (!srcFB || !dstFB)
|
||||
return;
|
||||
|
||||
const uint32_t srcID = srcFB->GetColorAttachmentRendererID();
|
||||
const uint32_t dstID = dstFB->GetColorAttachmentRendererID();
|
||||
const uint32_t width = dstFB->GetWidth();
|
||||
const uint32_t height = dstFB->GetHeight();
|
||||
|
||||
Renderer::Submit([srcID, dstID, width, height, srcFB]() mutable
|
||||
{
|
||||
// Use framebuffer blit to resolve multisampled source into non-multisampled luminance target.
|
||||
GLuint srcFBO = srcFB->GetRendererID();
|
||||
GLuint dstFBO = s_Data.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID();
|
||||
|
||||
// Bind read/draw FBOs and blit color attachment 0
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Source size — try to use srcFB dimensions if available
|
||||
int srcWidth = srcFB->GetWidth();
|
||||
int srcHeight = srcFB->GetHeight();
|
||||
|
||||
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// Unbind
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
// Generate mipmaps so the smallest mip is the average color
|
||||
glGenerateTextureMipmap(dstID);
|
||||
|
||||
// Determine highest mip level
|
||||
int maxLevel = (int)std::floor(std::log2((float)std::max(width, height)));
|
||||
if (maxLevel < 0) maxLevel = 0;
|
||||
|
||||
float pixel[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, sizeof(pixel), pixel);
|
||||
|
||||
// Sanitize pixel values (handle NaN/Inf or negative values coming from GPU)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (!std::isfinite(pixel[i]) || pixel[i] < 0.0f)
|
||||
pixel[i] = 0.0f;
|
||||
}
|
||||
|
||||
// Convert to luminance
|
||||
float lum = 0.2126f * pixel[0] + 0.7152f * pixel[1] + 0.0722f * pixel[2];
|
||||
if (!std::isfinite(lum) || lum <= 0.0f)
|
||||
lum = 1e-6f; // fallback minimum luminance
|
||||
|
||||
// Compute desired exposure (simple key/avg approach)
|
||||
const float key = s_Data.AutoExposure.Key;
|
||||
constexpr float minLum = 1e-6f;
|
||||
float desiredExposure = key / std::max(lum, minLum);
|
||||
desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
|
||||
|
||||
|
||||
// Adapt exposure over time (exponential)
|
||||
const float dt = s_Data.AutoExposure.ExposureTimer.ElapsedMillis() / 1000.0f;
|
||||
s_Data.AutoExposure.ExposureTimer.Reset();
|
||||
const float tau = s_Data.AutoExposure.AdaptationSpeed;
|
||||
const float adaptFactor = 1.0f - std::exp(-tau * dt);
|
||||
s_Data.AutoExposure.CurrentExposure = s_Data.AutoExposure.CurrentExposure + (desiredExposure - s_Data.AutoExposure.CurrentExposure) * adaptFactor;
|
||||
s_Data.AutoExposure.CurrentExposure = std::clamp(s_Data.AutoExposure.CurrentExposure, 0.0001f, s_Data.AutoExposure.MaxExposure);
|
||||
|
||||
// Write exposure directly into composite shader program uniform so the subsequent composite pass uses it
|
||||
/*
|
||||
if (const GLuint prog = s_Data.CompositeShader->GetRendererID())
|
||||
{
|
||||
const GLint loc = glGetUniformLocation(prog, "u_Exposure");
|
||||
if (loc >= 0)
|
||||
glProgramUniform1f(prog, loc, s_Data.CurrentExposure);
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SceneRenderer::Init()
|
||||
{
|
||||
@ -250,23 +183,17 @@ namespace Prism
|
||||
|
||||
RenderPassSpecification luminanceRenderPassSpec;
|
||||
luminanceRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(luminanceFramebufferSpec);
|
||||
s_Data.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
|
||||
|
||||
// Auto exposure defaults
|
||||
s_Data.AutoExposure.CurrentExposure = 1.0f;
|
||||
s_Data.AutoExposure.EnableAutoExposure = true;
|
||||
s_Data.AutoExposure.Key = 0.18f;
|
||||
s_Data.AutoExposure.AdaptationSpeed = 1.0f;
|
||||
s_Data.AutoExposure.ExposureTimer.Reset();
|
||||
s_Data.AutoExposureData.LuminancePass = RenderPass::Create(luminanceRenderPassSpec);
|
||||
|
||||
// Grid
|
||||
const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
|
||||
s_Data.GridMaterial = MaterialInstance::Create(Material::Create(gridShader));
|
||||
constexpr float gridScale = 16.025f;
|
||||
constexpr float gridSize = 0.025f;
|
||||
s_Data.GridMaterial->Set("u_Scale", gridScale);
|
||||
s_Data.GridMaterial->Set("u_Res", gridSize);
|
||||
s_Data.GridMaterial->SetFlag(MaterialFlag::TwoSided, true);
|
||||
// const auto gridShader = Shader::Create("assets/shaders/Grid.glsl");
|
||||
const auto gridShader = Shader::Create("assets/shaders/InfiniteGrid.glsl");
|
||||
s_Data.GridData.GridMaterial = MaterialInstance::Create(Material::Create(gridShader));
|
||||
// constexpr float gridScale = 16.025f;
|
||||
// constexpr float gridSize = 0.025f;
|
||||
// s_Data.GridMaterial->Set("u_Scale", gridScale);
|
||||
// s_Data.GridMaterial->Set("u_Res", gridSize);
|
||||
s_Data.GridData.GridMaterial->SetFlag(MaterialFlag::TwoSided, true);
|
||||
|
||||
// outline
|
||||
const auto outlineShader = Shader::Create("assets/shaders/Outline.glsl");
|
||||
@ -293,13 +220,9 @@ namespace Prism
|
||||
shadowMapFramebufferSpec.ClearColor = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
shadowMapFramebufferSpec.NoResize = true;
|
||||
|
||||
// 4 cascades
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
RenderPassSpecification shadowMapRenderPassSpec;
|
||||
shadowMapRenderPassSpec.TargetFramebuffer = FrameBuffer::Create(shadowMapFramebufferSpec);
|
||||
s_Data.ShadowMapRenderPass[i] = RenderPass::Create(shadowMapRenderPassSpec);
|
||||
}
|
||||
s_Data.ShadowMapRenderPass = RenderPass::Create(shadowMapRenderPassSpec);
|
||||
|
||||
Renderer::Submit([]()
|
||||
{
|
||||
@ -318,8 +241,8 @@ namespace Prism
|
||||
{
|
||||
s_Data.GeoPass->GetSpecification().TargetFramebuffer->Resize(width, height);
|
||||
s_Data.CompositePass->GetSpecification().TargetFramebuffer->Resize(width, height);
|
||||
if (s_Data.LuminancePass)
|
||||
s_Data.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height);
|
||||
if (s_Data.AutoExposureData.LuminancePass)
|
||||
s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->Resize(width, height);
|
||||
}
|
||||
|
||||
void SceneRenderer::BeginScene(const Scene* scene, const SceneRendererCamera& camera)
|
||||
@ -495,7 +418,7 @@ namespace Prism
|
||||
// Compute average luminance and update exposure (GPU-copy + mipmap -> read 1 texel)
|
||||
{
|
||||
Renderer::Submit([]() { s_Stats.AutoExposurePassTimer.Reset(); });
|
||||
ComputeAutoExposure();
|
||||
AutoExposurePass();
|
||||
Renderer::Submit([] { s_Stats.AutoExposurePass = s_Stats.AutoExposurePassTimer.ElapsedMillis(); });
|
||||
}
|
||||
|
||||
@ -503,7 +426,7 @@ namespace Prism
|
||||
Renderer::Submit([]() { s_Stats.CompositePassTimer.Reset(); });
|
||||
CompositePass();
|
||||
Renderer::Submit([] { s_Stats.CompositePass = s_Stats.CompositePassTimer.ElapsedMillis(); });
|
||||
BloomBlurPass();
|
||||
// BloomBlurPass();
|
||||
}
|
||||
|
||||
s_Data.DrawList.clear();
|
||||
@ -538,13 +461,15 @@ namespace Prism
|
||||
|
||||
const auto& sceneCamera = s_Data.SceneData.SceneCamera;
|
||||
|
||||
const auto viewProjection = sceneCamera.Camera.GetProjectionMatrix() * sceneCamera.ViewMatrix;
|
||||
const auto cameraProjection = sceneCamera.Camera.GetProjectionMatrix();
|
||||
const auto cameraView = sceneCamera.ViewMatrix;
|
||||
const auto cameraViewProjection = cameraProjection * cameraView;
|
||||
const glm::vec3 cameraPosition = glm::inverse(s_Data.SceneData.SceneCamera.ViewMatrix)[3]; // TODO: Negate instead
|
||||
|
||||
|
||||
// Skybox
|
||||
auto skyboxShader = s_Data.SceneData.SkyboxMaterial->GetShader();
|
||||
s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(viewProjection));
|
||||
s_Data.SceneData.SkyboxMaterial->Set("u_InverseVP", glm::inverse(cameraViewProjection));
|
||||
s_Data.SceneData.SkyboxMaterial->Set("u_SkyIntensity", s_Data.SceneData.SceneEnvironmentIntensity);
|
||||
// s_Data.SceneInfo.EnvironmentIrradianceMap->Bind(0);
|
||||
Renderer::SubmitFullscreenQuad(s_Data.SceneData.SkyboxMaterial);
|
||||
@ -556,22 +481,22 @@ namespace Prism
|
||||
for (auto& dc : s_Data.DrawList)
|
||||
{
|
||||
auto baseMaterial = dc.mesh->GetMaterial();
|
||||
baseMaterial->Set("u_ViewProjectionMatrix", viewProjection);
|
||||
baseMaterial->Set("u_ViewProjectionMatrix", cameraViewProjection);
|
||||
baseMaterial->Set("u_ViewMatrix", sceneCamera.ViewMatrix);
|
||||
baseMaterial->Set("u_CameraPosition", cameraPosition);
|
||||
baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
|
||||
baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
|
||||
baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
|
||||
baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
|
||||
baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
|
||||
baseMaterial->Set("u_LightView", s_Data.LightViewMatrix);
|
||||
baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
|
||||
baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
|
||||
baseMaterial->Set("u_LightSize", s_Data.LightSize);
|
||||
baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
|
||||
baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
|
||||
baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
|
||||
baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
|
||||
// baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
|
||||
// baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
|
||||
// baseMaterial->Set("u_LightView", s_Data.LightViewMatrix);
|
||||
// baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
|
||||
// baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
|
||||
// baseMaterial->Set("u_LightSize", s_Data.LightSize);
|
||||
// baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
|
||||
// baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
|
||||
// baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
|
||||
// baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
|
||||
baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity);
|
||||
|
||||
// Environment (TODO: don't do this per mesh)
|
||||
@ -580,9 +505,30 @@ namespace Prism
|
||||
baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT);
|
||||
|
||||
// Set lights (TODO: move to light environment and don't do per mesh)
|
||||
auto directionalLight = s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0];
|
||||
baseMaterial->Set("u_DirectionalLights", directionalLight);
|
||||
|
||||
baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]);
|
||||
// baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight);
|
||||
|
||||
// shadow
|
||||
baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices);
|
||||
baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled);
|
||||
baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias);
|
||||
baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity);
|
||||
|
||||
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap");
|
||||
if (rd)
|
||||
{
|
||||
auto reg = rd->GetRegister();
|
||||
auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
|
||||
|
||||
Renderer::Submit([reg, tex]() mutable
|
||||
{
|
||||
glBindTextureUnit(reg, tex);
|
||||
glBindSampler(reg++, s_Data.ShadowMapSampler);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture");
|
||||
if (rd)
|
||||
{
|
||||
@ -609,6 +555,7 @@ namespace Prism
|
||||
glBindSampler(reg++, s_Data.ShadowMapSampler);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
constexpr auto overrideMaterial = nullptr; // dc.Material;
|
||||
@ -626,32 +573,55 @@ namespace Prism
|
||||
for (auto& dc : s_Data.SelectedMeshDrawList)
|
||||
{
|
||||
auto baseMaterial = dc.mesh->GetMaterial();
|
||||
baseMaterial->Set("u_ViewProjectionMatrix", viewProjection);
|
||||
baseMaterial->Set("u_ViewProjectionMatrix", cameraViewProjection);
|
||||
baseMaterial->Set("u_ViewMatrix", sceneCamera.ViewMatrix);
|
||||
baseMaterial->Set("u_CameraPosition", cameraPosition);
|
||||
baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
|
||||
baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
|
||||
baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
|
||||
baseMaterial->Set("u_LightSize", s_Data.LightSize);
|
||||
baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
|
||||
baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
|
||||
baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
|
||||
baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
|
||||
// baseMaterial->Set("u_CascadeSplits", s_Data.CascadeSplits);
|
||||
// baseMaterial->Set("u_ShowCascades", s_Data.ShowCascades);
|
||||
// baseMaterial->Set("u_SoftShadows", s_Data.SoftShadows);
|
||||
// baseMaterial->Set("u_LightSize", s_Data.LightSize);
|
||||
// baseMaterial->Set("u_MaxShadowDistance", s_Data.MaxShadowDistance);
|
||||
// baseMaterial->Set("u_ShadowFade", s_Data.ShadowFade);
|
||||
// baseMaterial->Set("u_CascadeFading", s_Data.CascadeFading);
|
||||
// baseMaterial->Set("u_CascadeTransitionFade", s_Data.CascadeTransitionFade);
|
||||
baseMaterial->Set("u_IBLContribution", s_Data.SceneData.SceneEnvironmentIntensity);
|
||||
|
||||
|
||||
// Environment (TODO: don't do this per mesh)
|
||||
baseMaterial->Set("u_EnvRadianceTex", s_Data.SceneData.SceneEnvironment->RadianceMap);
|
||||
baseMaterial->Set("u_EnvIrradianceTex", s_Data.SceneData.SceneEnvironment->IrradianceMap);
|
||||
baseMaterial->Set("u_BRDFLUTTexture", s_Data.BRDFLUT);
|
||||
|
||||
baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
|
||||
baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
|
||||
baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
|
||||
baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade0", s_Data.LightMatrices[0]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade1", s_Data.LightMatrices[1]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade2", s_Data.LightMatrices[2]);
|
||||
// baseMaterial->Set("u_LightMatrixCascade3", s_Data.LightMatrices[3]);
|
||||
|
||||
// Set lights (TODO: move to light environment and don't do per mesh)
|
||||
baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.SceneLightEnvironment.DirectionalLights[0]);
|
||||
// baseMaterial->Set("u_DirectionalLights", s_Data.SceneData.ActiveLight);
|
||||
|
||||
|
||||
// shadow
|
||||
baseMaterial->Set("u_LightSpaceMatrix", s_Data.LightMatrices);
|
||||
baseMaterial->Set("u_ShadowEnabled", s_Data.ShadowEnabled);
|
||||
baseMaterial->Set("u_ShadowBias", s_Data.ShadowBias);
|
||||
baseMaterial->Set("u_ShadowIntensity", s_Data.ShadowIntensity);
|
||||
|
||||
|
||||
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMap");
|
||||
if (rd)
|
||||
{
|
||||
auto reg = rd->GetRegister();
|
||||
auto tex = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer->GetDepthAttachmentRendererID();
|
||||
|
||||
Renderer::Submit([reg, tex]() mutable
|
||||
{
|
||||
glBindTextureUnit(reg, tex);
|
||||
glBindSampler(reg, s_Data.ShadowMapSampler);
|
||||
});
|
||||
}
|
||||
/*
|
||||
auto rd = baseMaterial->FindResourceDeclaration("u_ShadowMapTexture");
|
||||
if (rd)
|
||||
{
|
||||
@ -678,6 +648,7 @@ namespace Prism
|
||||
glBindSampler(reg++, s_Data.ShadowMapSampler);
|
||||
});
|
||||
}
|
||||
*/
|
||||
|
||||
constexpr auto overrideMaterial = nullptr; // dc.Material;
|
||||
Renderer::SubmitMesh(dc.mesh, dc.Transform, overrideMaterial);
|
||||
@ -698,8 +669,8 @@ namespace Prism
|
||||
});
|
||||
|
||||
// Draw outline here
|
||||
s_Data.OutlineMaterial->Set("u_ViewProjection", viewProjection);
|
||||
s_Data.OutlineAnimMaterial->Set("u_ViewProjection", viewProjection);
|
||||
s_Data.OutlineMaterial->Set("u_ViewProjection", cameraViewProjection);
|
||||
s_Data.OutlineAnimMaterial->Set("u_ViewProjection", cameraViewProjection);
|
||||
for (auto& dc : s_Data.SelectedMeshDrawList)
|
||||
{
|
||||
// Renderer::SubmitMesh(dc.mesh, dc.Transform, s_Data.OutlineMaterial);
|
||||
@ -739,7 +710,7 @@ namespace Prism
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
});
|
||||
|
||||
s_Data.ColliderMaterial->Set("u_ViewProjection", viewProjection);
|
||||
s_Data.ColliderMaterial->Set("u_ViewProjection", cameraViewProjection);
|
||||
for (auto& dc : s_Data.ColliderDrawList)
|
||||
{
|
||||
if (dc.mesh)
|
||||
@ -772,13 +743,25 @@ namespace Prism
|
||||
const auto option = GetOptions();
|
||||
if (option.ShowGrid)
|
||||
{
|
||||
s_Data.GridMaterial->Set("u_ViewProjection", viewProjection);
|
||||
Renderer::SubmitQuad(s_Data.GridMaterial, glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)) * glm::scale(glm::mat4(1.0f), glm::vec3(16.0f)));
|
||||
s_Data.GridData.GridMaterial->Set("u_View", sceneCamera.ViewMatrix);
|
||||
s_Data.GridData.GridMaterial->Set("u_Projection", cameraProjection);
|
||||
s_Data.GridData.GridMaterial->Set("u_CameraPosition", cameraPosition);
|
||||
|
||||
// grid config
|
||||
s_Data.GridData.GridMaterial->Set("u_GridPlane", s_Data.GridData.GridPlane);
|
||||
s_Data.GridData.GridMaterial->Set("u_GridScale", s_Data.GridData.GridScale);
|
||||
s_Data.GridData.GridMaterial->Set("u_GridColorThin", s_Data.GridData.GridColorThin);
|
||||
s_Data.GridData.GridMaterial->Set("u_GridColorThick", s_Data.GridData.GridColorThick);
|
||||
s_Data.GridData.GridMaterial->Set("u_AxisColorX", s_Data.GridData.AxisColorX);
|
||||
s_Data.GridData.GridMaterial->Set("u_AxisColorZ", s_Data.GridData.AxisColorZ);
|
||||
s_Data.GridData.GridMaterial->Set("u_FadeDistance", s_Data.GridData.FadeDistance);
|
||||
|
||||
Renderer::SubmitFullscreenQuad(s_Data.GridData.GridMaterial);
|
||||
}
|
||||
|
||||
if (option.ShowBoundingBoxes)
|
||||
{
|
||||
Renderer2D::BeginScene(viewProjection);
|
||||
Renderer2D::BeginScene(cameraViewProjection);
|
||||
for (auto& dc : s_Data.DrawList)
|
||||
Renderer::DrawAABB(dc.mesh, dc.Transform);
|
||||
Renderer2D::EndScene();
|
||||
@ -787,13 +770,108 @@ namespace Prism
|
||||
Renderer::EndRenderPass();
|
||||
}
|
||||
|
||||
void SceneRenderer::AutoExposurePass()
|
||||
{
|
||||
if (!s_Data.AutoExposureData.EnableAutoExposure)
|
||||
return;
|
||||
|
||||
auto srcFB = s_Data.GeoPass->GetSpecification().TargetFramebuffer;
|
||||
auto dstFB = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer;
|
||||
if (!srcFB || !dstFB)
|
||||
return;
|
||||
|
||||
const uint32_t dstID = dstFB->GetColorAttachmentRendererID();
|
||||
const uint32_t width = dstFB->GetWidth();
|
||||
const uint32_t height = dstFB->GetHeight();
|
||||
|
||||
Renderer::Submit([dstID, width, height, srcFB]() mutable
|
||||
{
|
||||
// Use framebuffer blit to resolve multisampled source into non-multisampled luminance target.
|
||||
const GLuint srcFBO = srcFB->GetRendererID();
|
||||
const GLuint dstFBO = s_Data.AutoExposureData.LuminancePass->GetSpecification().TargetFramebuffer->GetRendererID();
|
||||
|
||||
// Bind read/draw FBOs and blit color attachment 0
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFBO);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dstFBO);
|
||||
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
// Source size — try to use srcFB dimensions if available
|
||||
const int srcWidth = srcFB->GetWidth();
|
||||
const int srcHeight = srcFB->GetHeight();
|
||||
|
||||
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||
|
||||
// Unbind
|
||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
|
||||
// Generate mipmaps so the smallest mip is the average color
|
||||
glGenerateTextureMipmap(dstID);
|
||||
|
||||
// Determine highest mip level
|
||||
int maxLevel = (int)std::floor(std::log2((float)std::max(width, height)));
|
||||
if (maxLevel < 0) maxLevel = 0;
|
||||
|
||||
|
||||
GLint levelWidth = 0, levelHeight = 0;
|
||||
glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_WIDTH, &levelWidth);
|
||||
glGetTextureLevelParameteriv(dstID, maxLevel, GL_TEXTURE_HEIGHT, &levelHeight);
|
||||
|
||||
if (levelWidth == 0 || levelHeight == 0) return;
|
||||
|
||||
const int bufSize = levelWidth * levelHeight * 4 * sizeof(float);
|
||||
std::vector<float> pixelData(bufSize / sizeof(float));
|
||||
|
||||
glGetTextureImage(dstID, maxLevel, GL_RGBA, GL_FLOAT, bufSize, pixelData.data());
|
||||
|
||||
// Sanitize pixel values (handle NaN/Inf or negative values coming from GPU)
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
if (!std::isfinite(pixelData[i]) || pixelData[i] < 0.0f)
|
||||
pixelData[i] = 0.0f;
|
||||
}
|
||||
|
||||
// Convert to luminance
|
||||
float lum = 0.2126f * pixelData[0] + 0.7152f * pixelData[1] + 0.0722f * pixelData[2];
|
||||
if (!std::isfinite(lum) || lum <= 0.0f)
|
||||
lum = 1e-6f; // fallback minimum luminance
|
||||
|
||||
// Compute desired exposure (simple key/avg approach)
|
||||
const float key = s_Data.AutoExposureData.Key;
|
||||
constexpr float minLum = 1e-6f;
|
||||
float desiredExposure = key / std::max(lum, minLum);
|
||||
desiredExposure = std::clamp(desiredExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure);
|
||||
|
||||
|
||||
// Adapt exposure over time (exponential)
|
||||
const float dt = s_Data.AutoExposureData.ExposureTimer.ElapsedMillis() / 1000.0f;
|
||||
s_Data.AutoExposureData.ExposureTimer.Reset();
|
||||
const float tau = s_Data.AutoExposureData.AdaptationSpeed;
|
||||
const float adaptFactor = 1.0f - std::exp(-tau * dt);
|
||||
s_Data.AutoExposureData.CurrentExposure = s_Data.AutoExposureData.CurrentExposure + (desiredExposure - s_Data.AutoExposureData.CurrentExposure) * adaptFactor;
|
||||
s_Data.AutoExposureData.CurrentExposure = std::clamp(s_Data.AutoExposureData.CurrentExposure, 0.0001f, s_Data.AutoExposureData.MaxExposure);
|
||||
|
||||
// Write exposure directly into composite shader program uniform so the subsequent composite pass uses it
|
||||
/*
|
||||
if (const GLuint prog = s_Data.CompositeShader->GetRendererID())
|
||||
{
|
||||
const GLint loc = glGetUniformLocation(prog, "u_Exposure");
|
||||
if (loc >= 0)
|
||||
glProgramUniform1f(prog, loc, s_Data.CurrentExposure);
|
||||
}
|
||||
*/
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
void SceneRenderer::CompositePass()
|
||||
{
|
||||
auto& compositeBuffer = s_Data.CompositePass->GetSpecification().TargetFramebuffer;
|
||||
|
||||
Renderer::BeginRenderPass(s_Data.CompositePass);
|
||||
s_Data.CompositeShader->Bind();
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetInt("u_TextureSamples", s_Data.GeoPass->GetSpecification().TargetFramebuffer->GetSpecification().Samples);
|
||||
// s_Data.CompositeShader->SetFloat2("u_ViewportSize", glm::vec2(compositeBuffer->GetWidth(), compositeBuffer->GetHeight()));
|
||||
// s_Data.CompositeShader->SetFloat2("u_FocusPoint", s_Data.FocusPoint);
|
||||
@ -851,7 +929,7 @@ namespace Prism
|
||||
s_Data.BloomBlendShader->Bind();
|
||||
auto & adad =s_Data;
|
||||
// s_Data.BloomBlendShader->SetFloat("u_Exposure", s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposure.EnableAutoExposure ? s_Data.AutoExposure.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.CompositeShader->SetFloat("u_Exposure", s_Data.AutoExposureData.EnableAutoExposure ? s_Data.AutoExposureData.CurrentExposure : s_Data.SceneData.SceneCamera.Camera.GetExposure());
|
||||
s_Data.BloomBlendShader->SetBool("u_EnableBloom", s_Data.EnableBloom);
|
||||
|
||||
s_Data.CompositePass->GetSpecification().TargetFramebuffer->BindTexture(0);
|
||||
@ -874,6 +952,7 @@ namespace Prism
|
||||
float SplitDepth;
|
||||
};
|
||||
|
||||
|
||||
static void CalculateCascades(CascadeData* cascades, const glm::vec3& lightDirection)
|
||||
{
|
||||
// FrustumBounds frustumBounds[3];
|
||||
@ -915,7 +994,7 @@ namespace Prism
|
||||
// cascadeSplits[3] = 1.0f;
|
||||
|
||||
// Calculate orthographic projection matrix for each cascade
|
||||
float lastSplitDist = 0.0;
|
||||
float lastSplitDist = 1.0;
|
||||
for (uint32_t i = 0; i < SHADOW_MAP_CASCADE_COUNT; i++)
|
||||
{
|
||||
float splitDist = cascadeSplits[i];
|
||||
@ -1000,15 +1079,21 @@ namespace Prism
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
// Clear shadow maps
|
||||
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]);
|
||||
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass);
|
||||
Renderer::EndRenderPass();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
CascadeData cascades[4];
|
||||
CalculateCascades(cascades, directionalLights[0].Direction);
|
||||
s_Data.LightViewMatrix = cascades[0].View;
|
||||
glm::vec3 lightDir = glm::normalize(directionalLights[0].Direction); // 光线方向(从光源指向场景)
|
||||
glm::vec3 lightPos = lightDir * 10.0f;
|
||||
|
||||
glm::mat4 lightView = glm::lookAt(lightPos, glm::vec3(0.0f), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
|
||||
float orthoSize = 100.0f;
|
||||
float nearPlane = 0.01f, farPlane = 1000.0f;
|
||||
glm::mat4 lightProjection = glm::ortho(-orthoSize, orthoSize, -orthoSize, orthoSize, nearPlane, farPlane);
|
||||
glm::mat4 lightSpaceMatrix = lightProjection * lightView;
|
||||
|
||||
Renderer::Submit([]()
|
||||
{
|
||||
@ -1016,26 +1101,18 @@ namespace Prism
|
||||
glCullFace(GL_BACK);
|
||||
});
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
s_Data.CascadeSplits[i] = cascades[i].SplitDepth;
|
||||
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass);
|
||||
|
||||
Renderer::BeginRenderPass(s_Data.ShadowMapRenderPass[i]);
|
||||
|
||||
glm::mat4 shadowMapVP = cascades[i].ViewProj;
|
||||
// s_Data.ShadowMapShader->SetMat4("u_ViewProjection", shadowMapVP);
|
||||
|
||||
static glm::mat4 scaleBiasMatrix = glm::scale(glm::mat4(1.0f), { 0.5f, 0.5f, 0.5f }) * glm::translate(glm::mat4(1.0f), { 1, 1, 1 });
|
||||
s_Data.LightMatrices[i] = scaleBiasMatrix * cascades[i].ViewProj;
|
||||
s_Data.LightMatrices = lightSpaceMatrix;
|
||||
|
||||
|
||||
// Render entities
|
||||
for (auto& dc : s_Data.ShadowPassDrawList)
|
||||
{
|
||||
Ref<Shader> shader = dc.mesh->IsAnimated() ? s_Data.ShadowMapAnimShader : s_Data.ShadowMapShader;
|
||||
shader->SetMat4("u_ViewProjection", shadowMapVP);
|
||||
shader->SetMat4("u_ViewProjection", lightSpaceMatrix);
|
||||
Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, shader);
|
||||
// Renderer::SubmitMeshWithShader(dc.mesh, dc.Transform, s_Data.ShadowMapShader);
|
||||
}
|
||||
|
||||
Renderer::EndRenderPass();
|
||||
@ -1046,40 +1123,41 @@ namespace Prism
|
||||
{
|
||||
ImGui::Begin("Scene Renderer");
|
||||
|
||||
if (UI::BeginTreeNode("Shadows"))
|
||||
{
|
||||
UI::Property("Composite Pass time: %.2fs", s_Stats.CompositePass);
|
||||
UI::Property("Geometry Pass time: %.2fs", s_Stats.GeometryPass);
|
||||
UI::Property("Shadow Pass time: %.2fs", s_Stats.ShadowPass);
|
||||
UI::Property("Composite Pass time", s_Stats.CompositePass);
|
||||
UI::Property("Geometry Pass time", s_Stats.GeometryPass);
|
||||
UI::Property("Shadow Pass time", s_Stats.ShadowPass);
|
||||
UI::Property("AutoExposure Pass time", s_Stats.AutoExposurePass);
|
||||
|
||||
if (UI::BeginTreeNode("Grid Config", false))
|
||||
{
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("Soft Shadows", s_Data.SoftShadows);
|
||||
UI::Property("Light Size", s_Data.LightSize, 0.01f);
|
||||
UI::Property("Max Shadow Distance", s_Data.MaxShadowDistance, 1.0f);
|
||||
UI::Property("Shadow Fade", s_Data.ShadowFade, 5.0f);
|
||||
UI::PropertySlider("GridPlane", s_Data.GridData.GridPlane, 0, 2);
|
||||
UI::Property("GridScale", s_Data.GridData.GridScale);
|
||||
UI::PropertyColor("GridColorThin", s_Data.GridData.GridColorThin);
|
||||
UI::PropertyColor("GridColorThick", s_Data.GridData.GridColorThick);
|
||||
UI::PropertyColor("AxisColorX", s_Data.GridData.AxisColorX);
|
||||
UI::PropertyColor("AxisColorZ", s_Data.GridData.AxisColorZ);
|
||||
UI::Property("FadeDistance ", s_Data.GridData.FadeDistance);
|
||||
UI::EndPropertyGrid();
|
||||
if (UI::BeginTreeNode("Cascade Settings"))
|
||||
{
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("Show Cascades", s_Data.ShowCascades);
|
||||
UI::Property("Cascade Fading", s_Data.CascadeFading);
|
||||
UI::Property("Cascade Transition Fade", s_Data.CascadeTransitionFade, 0.05f, 0.0f, FLT_MAX);
|
||||
UI::Property("Cascade Split", s_Data.CascadeSplitLambda, 0.01f);
|
||||
UI::Property("CascadeNearPlaneOffset", s_Data.CascadeNearPlaneOffset, 0.1f, -FLT_MAX, 0.0f);
|
||||
UI::Property("CascadeFarPlaneOffset", s_Data.CascadeFarPlaneOffset, 0.1f, 0.0f, FLT_MAX);
|
||||
UI::EndPropertyGrid();
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
|
||||
if (UI::BeginTreeNode("Shadows", false))
|
||||
{
|
||||
if (UI::BeginTreeNode("Shadow Map", false))
|
||||
{
|
||||
static int cascadeIndex = 0;
|
||||
auto fb = s_Data.ShadowMapRenderPass[cascadeIndex]->GetSpecification().TargetFramebuffer;
|
||||
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("EnableMap", s_Data.ShadowEnabled);
|
||||
UI::Property("ShadowBias", s_Data.ShadowBias, 0.01f);
|
||||
UI::Property("ShadowSoftness", s_Data.ShadowSoftness);
|
||||
UI::Property("ShadowIntensity", s_Data.ShadowIntensity, 0.01f);
|
||||
UI::EndPropertyGrid();
|
||||
|
||||
auto fb = s_Data.ShadowMapRenderPass->GetSpecification().TargetFramebuffer;
|
||||
auto id = fb->GetDepthAttachmentRendererID();
|
||||
|
||||
float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
|
||||
UI::BeginPropertyGrid();
|
||||
UI::PropertySlider("Cascade Index", cascadeIndex, 0, 3);
|
||||
UI::EndPropertyGrid();
|
||||
ImGui::Image((ImTextureID)id, { size, size }, { 0, 1 }, { 1, 0 });
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
@ -1087,7 +1165,7 @@ namespace Prism
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
|
||||
if (UI::BeginTreeNode("Bloom"))
|
||||
if (UI::BeginTreeNode("Bloom", false))
|
||||
{
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("Bloom", s_Data.EnableBloom);
|
||||
@ -1095,24 +1173,22 @@ namespace Prism
|
||||
UI::EndPropertyGrid();
|
||||
|
||||
auto fb = s_Data.BloomBlurPass[0]->GetSpecification().TargetFramebuffer;
|
||||
auto id = fb->GetColorAttachmentRendererID();
|
||||
const auto id = fb->GetColorAttachmentRendererID();
|
||||
|
||||
float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
|
||||
const float size = ImGui::GetContentRegionAvail().x; // (float)fb->GetWidth() * 0.5f, (float)fb->GetHeight() * 0.5f
|
||||
float w = size;
|
||||
float h = w / ((float)fb->GetWidth() / (float)fb->GetHeight());
|
||||
ImGui::Image((ImTextureID)id, { w, h }, { 0, 1 }, { 1, 0 });
|
||||
UI::EndTreeNode();
|
||||
}
|
||||
|
||||
if (UI::BeginTreeNode("Auto Exposure"))
|
||||
if (UI::BeginTreeNode("Auto Exposure", false))
|
||||
{
|
||||
UI::Property("Auto Exposure Pass time: %.2fs", s_Stats.AutoExposurePass);
|
||||
|
||||
UI::BeginPropertyGrid();
|
||||
UI::Property("Enable Auto Exposure", s_Data.AutoExposure.EnableAutoExposure);
|
||||
UI::Property("Key (middle gray)", s_Data.AutoExposure.Key, 0.001f, 0.001f, 2.5f);
|
||||
UI::Property("Adaptation Speed", s_Data.AutoExposure.AdaptationSpeed, 0.01f, 0.001f, 5.0f);
|
||||
UI::Property("Current Exposure", s_Data.AutoExposure.CurrentExposure, 0.01f, 0.0f, 0.0f, true);
|
||||
UI::Property("Enable Auto Exposure", s_Data.AutoExposureData.EnableAutoExposure);
|
||||
UI::Property("Key (middle gray)", s_Data.AutoExposureData.Key, 0.001f, 0.001f, 2.5f);
|
||||
UI::Property("Adaptation Speed", s_Data.AutoExposureData.AdaptationSpeed, 0.01f, 0.001f, 5.0f);
|
||||
UI::Property("Current Exposure", s_Data.AutoExposureData.CurrentExposure, 0.01f, 0.0f, 0.0f, true);
|
||||
UI::EndPropertyGrid();
|
||||
|
||||
UI::EndTreeNode();
|
||||
|
||||
@ -32,7 +32,6 @@ namespace Prism
|
||||
{
|
||||
public:
|
||||
static void Init();
|
||||
void InitAutoExposure();
|
||||
|
||||
static void SetViewportSize(uint32_t width, uint32_t height);
|
||||
|
||||
@ -62,6 +61,7 @@ namespace Prism
|
||||
static void OnImGuiRender();
|
||||
private:
|
||||
static void FlushDrawList();
|
||||
static void AutoExposurePass();
|
||||
static void GeometryPass();
|
||||
static void CompositePass();
|
||||
static void BloomBlurPass();
|
||||
|
||||
Reference in New Issue
Block a user