feat: add forward+ rendering
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
#version 460
|
||||
|
||||
#extension GL_GOOGLE_include_directive : require
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
#extension GL_EXT_scalar_block_layout : require
|
||||
|
||||
#include "bindless.glsl"
|
||||
#include "scene_data.glsl"
|
||||
|
||||
// Forward+ light culling.
|
||||
// One workgroup per LIGHT_TILE_SIZE x LIGHT_TILE_SIZE screen tile:
|
||||
// 1. find the min/max depth of the tile from the depth pre-pass
|
||||
// 2. build the tile frustum in view space
|
||||
// 3. test every light against it and write the visible ones to the tile's light list
|
||||
|
||||
layout (push_constant, scalar) uniform constants
|
||||
{
|
||||
SceneDataBuffer sceneData;
|
||||
uvec2 screenSize;
|
||||
uint depthTexID;
|
||||
uint padding;
|
||||
} pcs;
|
||||
|
||||
layout (local_size_x = LIGHT_TILE_SIZE, local_size_y = LIGHT_TILE_SIZE, local_size_z = 1) in;
|
||||
|
||||
#define THREADS_PER_TILE (LIGHT_TILE_SIZE * LIGHT_TILE_SIZE)
|
||||
|
||||
shared uint tileMinDepth;
|
||||
shared uint tileMaxDepth;
|
||||
shared uint tileLightCount;
|
||||
|
||||
// left, right, bottom, top side planes (view space, through the camera origin)
|
||||
shared vec4 tilePlanes[4];
|
||||
// view space distances along -Z
|
||||
shared float tileNearDistance;
|
||||
shared float tileFarDistance;
|
||||
|
||||
vec3 unprojectToView(vec2 ndc, float depth)
|
||||
{
|
||||
vec4 p = pcs.sceneData.invProj * vec4(ndc, depth, 1.0);
|
||||
return p.xyz / p.w;
|
||||
}
|
||||
|
||||
// Plane through the camera origin and two points, normal facing towards `inside`
|
||||
vec4 makeSidePlane(vec3 a, vec3 b, vec3 inside)
|
||||
{
|
||||
vec3 n = normalize(cross(a, b));
|
||||
if (dot(n, inside) < 0.0) {
|
||||
n = -n;
|
||||
}
|
||||
return vec4(n, 0.0);
|
||||
}
|
||||
|
||||
bool sphereInsideTile(vec3 center, float radius)
|
||||
{
|
||||
float distance = -center.z;
|
||||
if (distance + radius < tileNearDistance || distance - radius > tileFarDistance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (dot(tilePlanes[i].xyz, center) + tilePlanes[i].w < -radius) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void main()
|
||||
{
|
||||
uint localIndex = gl_LocalInvocationIndex;
|
||||
uvec2 tileID = gl_WorkGroupID.xy;
|
||||
uint tileIndex = tileID.y * pcs.sceneData.tileCountX + tileID.x;
|
||||
uint tileOffset = tileIndex * LIGHT_TILE_STRIDE;
|
||||
|
||||
if (localIndex == 0) {
|
||||
tileMinDepth = 0xFFFFFFFFu;
|
||||
tileMaxDepth = 0u;
|
||||
tileLightCount = 0u;
|
||||
}
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// 1. min / max depth
|
||||
uvec2 pixel = gl_GlobalInvocationID.xy;
|
||||
if (all(lessThan(pixel, pcs.screenSize))) {
|
||||
float depth = texelFetch(
|
||||
sampler2D(textures[pcs.depthTexID], samplers[NEAREST_SAMPLER_ID]),
|
||||
ivec2(pixel),
|
||||
0).r;
|
||||
|
||||
// depth == 1.0 is the cleared far plane (sky), nothing there receives light.
|
||||
// Depth is always positive, so its bits can be compared as uints.
|
||||
if (depth < 1.0) {
|
||||
uint depthBits = floatBitsToUint(depth);
|
||||
atomicMin(tileMinDepth, depthBits);
|
||||
atomicMax(tileMaxDepth, depthBits);
|
||||
}
|
||||
}
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// 2. tile frustum
|
||||
bool tileHasGeometry = tileMinDepth != 0xFFFFFFFFu;
|
||||
|
||||
if (localIndex == 0 && tileHasGeometry) {
|
||||
vec2 screenSize = vec2(pcs.screenSize);
|
||||
vec2 ndcMin = vec2(tileID * LIGHT_TILE_SIZE) / screenSize * 2.0 - 1.0;
|
||||
vec2 ndcMax = vec2((tileID + 1) * LIGHT_TILE_SIZE) / screenSize * 2.0 - 1.0;
|
||||
vec2 ndcCenter = (ndcMin + ndcMax) * 0.5;
|
||||
|
||||
vec3 corner00 = unprojectToView(vec2(ndcMin.x, ndcMin.y), 1.0);
|
||||
vec3 corner10 = unprojectToView(vec2(ndcMax.x, ndcMin.y), 1.0);
|
||||
vec3 corner11 = unprojectToView(vec2(ndcMax.x, ndcMax.y), 1.0);
|
||||
vec3 corner01 = unprojectToView(vec2(ndcMin.x, ndcMax.y), 1.0);
|
||||
vec3 center = unprojectToView(ndcCenter, 1.0);
|
||||
|
||||
tilePlanes[0] = makeSidePlane(corner01, corner00, center);
|
||||
tilePlanes[1] = makeSidePlane(corner10, corner11, center);
|
||||
tilePlanes[2] = makeSidePlane(corner00, corner10, center);
|
||||
tilePlanes[3] = makeSidePlane(corner11, corner01, center);
|
||||
|
||||
tileNearDistance = -unprojectToView(ndcCenter, uintBitsToFloat(tileMinDepth)).z;
|
||||
tileFarDistance = -unprojectToView(ndcCenter, uintBitsToFloat(tileMaxDepth)).z;
|
||||
}
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
// 3. cull lights, every thread takes a slice of the light list
|
||||
if (tileHasGeometry) {
|
||||
for (uint i = localIndex; i < pcs.sceneData.numLights; i += THREADS_PER_TILE) {
|
||||
Light light = pcs.sceneData.lights.data[i];
|
||||
|
||||
bool visible = light.type == TYPE_DIRECTIONAL_LIGHT;
|
||||
if (!visible) {
|
||||
vec3 viewPos = (pcs.sceneData.view * vec4(light.position, 1.0)).xyz;
|
||||
visible = sphereInsideTile(viewPos, light.range);
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
uint slot = atomicAdd(tileLightCount, 1u);
|
||||
if (slot < MAX_LIGHTS_PER_TILE) {
|
||||
pcs.sceneData.tileLightIndices.data[tileOffset + 1 + slot] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
memoryBarrierShared();
|
||||
barrier();
|
||||
|
||||
if (localIndex == 0) {
|
||||
pcs.sceneData.tileLightIndices.data[tileOffset] = min(tileLightCount, uint(MAX_LIGHTS_PER_TILE));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
#ifndef LIGHTING_GLSL
|
||||
#define LIGHTING_GLSL
|
||||
|
||||
#include "lights.glsl"
|
||||
|
||||
#define PI 3.14159265359
|
||||
|
||||
// GGX / Trowbridge-Reitz normal distribution
|
||||
float distributionGGX(float NoH, float roughness)
|
||||
{
|
||||
float a = roughness * roughness;
|
||||
float a2 = a * a;
|
||||
float d = NoH * NoH * (a2 - 1.0) + 1.0;
|
||||
return a2 / (PI * d * d);
|
||||
}
|
||||
|
||||
// Smith-Schlick GGX, k remapped for direct lighting
|
||||
float geometrySmithGGX(float NoV, float NoL, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0;
|
||||
float k = (r * r) / 8.0;
|
||||
float ggxV = NoV / (NoV * (1.0 - k) + k);
|
||||
float ggxL = NoL / (NoL * (1.0 - k) + k);
|
||||
return ggxV * ggxL;
|
||||
}
|
||||
|
||||
vec3 fresnelSchlick(float VoH, vec3 f0)
|
||||
{
|
||||
return f0 + (1.0 - f0) * pow(1.0 - VoH, 5.0);
|
||||
}
|
||||
|
||||
// Inverse square falloff, windowed so it reaches exactly 0 at the light range.
|
||||
// The range is also the sphere the light culling uses, so no light gets cut off at a tile edge.
|
||||
float calculateDistanceAttenuation(float distance, float range)
|
||||
{
|
||||
float window = clamp(1.0 - pow(distance / range, 4.0), 0.0, 1.0);
|
||||
return (window * window) / max(distance * distance, 0.0001);
|
||||
}
|
||||
|
||||
float calculateSpotAttenuation(Light light, vec3 l)
|
||||
{
|
||||
float cd = dot(normalize(light.direction), -l);
|
||||
return smoothstep(light.spotOuterCos, light.spotInnerCos, cd);
|
||||
}
|
||||
|
||||
vec3 calculateLight(Light light, vec3 fragPos, vec3 n, vec3 v,
|
||||
vec3 diffuseColor, float roughness, float metallic, vec3 f0)
|
||||
{
|
||||
vec3 l;
|
||||
float attenuation = 1.0;
|
||||
|
||||
if (light.type == TYPE_DIRECTIONAL_LIGHT) {
|
||||
l = normalize(-light.direction);
|
||||
} else {
|
||||
vec3 toLight = light.position - fragPos;
|
||||
float distance = length(toLight);
|
||||
l = toLight / max(distance, 0.0001);
|
||||
|
||||
attenuation = calculateDistanceAttenuation(distance, light.range);
|
||||
if (light.type == TYPE_SPOT_LIGHT) {
|
||||
attenuation *= calculateSpotAttenuation(light, l);
|
||||
}
|
||||
}
|
||||
|
||||
float NoL = clamp(dot(n, l), 0.0, 1.0);
|
||||
if (NoL <= 0.0 || attenuation <= 0.0) {
|
||||
return vec3(0.0);
|
||||
}
|
||||
|
||||
vec3 h = normalize(v + l);
|
||||
float NoV = clamp(dot(n, v), 0.0001, 1.0);
|
||||
float NoH = clamp(dot(n, h), 0.0, 1.0);
|
||||
float VoH = clamp(dot(v, h), 0.0, 1.0);
|
||||
|
||||
float D = distributionGGX(NoH, roughness);
|
||||
float G = geometrySmithGGX(NoV, NoL, roughness);
|
||||
vec3 F = fresnelSchlick(VoH, f0);
|
||||
|
||||
vec3 specular = (D * G * F) / (4.0 * NoV * NoL + 0.0001);
|
||||
vec3 kd = (vec3(1.0) - F) * (1.0 - metallic);
|
||||
vec3 diffuse = kd * diffuseColor / PI;
|
||||
|
||||
return (diffuse + specular) * light.color * light.intensity * attenuation * NoL;
|
||||
}
|
||||
|
||||
// blue -> green -> red, used to visualise how many lights a tile has
|
||||
vec3 lightHeatmapColor(uint lightCount)
|
||||
{
|
||||
float t = clamp(float(lightCount) / 16.0, 0.0, 1.0);
|
||||
if (t < 0.5) {
|
||||
return mix(vec3(0.0, 0.0, 1.0), vec3(0.0, 1.0, 0.0), t * 2.0);
|
||||
}
|
||||
return mix(vec3(0.0, 1.0, 0.0), vec3(1.0, 0.0, 0.0), (t - 0.5) * 2.0);
|
||||
}
|
||||
|
||||
#endif // LIGHTING_GLSL
|
||||
@@ -0,0 +1,41 @@
|
||||
#ifndef LIGHTS_GLSL
|
||||
#define LIGHTS_GLSL
|
||||
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
#define TYPE_DIRECTIONAL_LIGHT 0
|
||||
#define TYPE_POINT_LIGHT 1
|
||||
#define TYPE_SPOT_LIGHT 2
|
||||
|
||||
// Forward+ tiling, must match LightCullingPipeline::TILE_SIZE / MAX_LIGHTS_PER_TILE
|
||||
#define LIGHT_TILE_SIZE 16
|
||||
#define MAX_LIGHTS_PER_TILE 255
|
||||
// Every tile stores its light count first, followed by MAX_LIGHTS_PER_TILE light indices
|
||||
#define LIGHT_TILE_STRIDE (MAX_LIGHTS_PER_TILE + 1)
|
||||
|
||||
// Must match GPULight in Light.h
|
||||
struct Light {
|
||||
vec3 position;
|
||||
float range;
|
||||
|
||||
vec3 color;
|
||||
float intensity;
|
||||
|
||||
// direction the light is pointing in (directional + spot lights)
|
||||
vec3 direction;
|
||||
uint type;
|
||||
|
||||
float spotInnerCos;
|
||||
float spotOuterCos;
|
||||
vec2 padding;
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) readonly buffer LightsBuffer {
|
||||
Light data[];
|
||||
};
|
||||
|
||||
layout (buffer_reference, std430) buffer TileLightIndicesBuffer {
|
||||
uint data[];
|
||||
};
|
||||
|
||||
#endif // LIGHTS_GLSL
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "bindless.glsl"
|
||||
#include "scene_data.glsl"
|
||||
#include "mesh_pcs.glsl"
|
||||
#include "lighting.glsl"
|
||||
|
||||
layout (location = 0) in vec3 inPos;
|
||||
layout (location = 1) in vec2 inUV;
|
||||
@@ -20,16 +21,13 @@ void main()
|
||||
uint samplerID = pcs.sceneData.materials.data[pcs.materialID].textureFilteringMode;
|
||||
|
||||
vec4 diffuse = sampleTexture2D(material.diffuseTex, samplerID, inUV) * material.baseColor;
|
||||
outFragColor = diffuse;
|
||||
|
||||
vec3 baseColor = diffuse.rgb;
|
||||
|
||||
// if (diffuse.a < 0.1) {
|
||||
// discard;
|
||||
// }
|
||||
//
|
||||
// vec3 baseColor = material.baseColor.rgb * diffuse.rgb;
|
||||
//
|
||||
// vec3 normal = normalize(inNormal).rgb;
|
||||
|
||||
vec3 normal = normalize(inNormal);
|
||||
// if (inTangent != vec4(0.0)) {
|
||||
// // FIXME: sometimes Blender doesn't export tangents for some objects
|
||||
// // for some reason. When we will start computing tangents manually,
|
||||
@@ -39,91 +37,65 @@ void main()
|
||||
// normal = inTBN * normalize(normal * 2.0 - 1.0);
|
||||
// normal = normalize(normal);
|
||||
// }
|
||||
//
|
||||
// #ifdef PBR
|
||||
// float metallicF = material.metallicRoughnessEmissive.r;
|
||||
// float roughnessF = material.metallicRoughnessEmissive.g;
|
||||
//
|
||||
// vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
||||
//
|
||||
// float roughness = roughnessF * metallicRoughness.g;
|
||||
// // roughness *= roughness; // from perceptual to linear
|
||||
// roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
||||
//
|
||||
// float metallic = metallicF * metallicRoughness.b;
|
||||
//
|
||||
// vec3 dielectricSpecular = vec3(0.04);
|
||||
// vec3 black = vec3(0.0);
|
||||
// vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
|
||||
// vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
|
||||
// #else
|
||||
// vec3 diffuseColor = baseColor;
|
||||
// float metallic = 0.f;
|
||||
// float roughness = 1.f;
|
||||
// vec3 f0 = vec3(0.0);
|
||||
// #endif
|
||||
//
|
||||
// vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
||||
// vec3 fragPos = inPos.xyz;
|
||||
// vec3 n = normal;
|
||||
// vec3 v = normalize(cameraPos - fragPos);
|
||||
//
|
||||
// // diffuseColor = vec3(1.0);
|
||||
// // baseColor = vec3(1.0);
|
||||
//
|
||||
// vec3 fragColor = vec3(0.0);
|
||||
// for (int i = 0; i < pcs.sceneData.numLights; i++) {
|
||||
// Light light = pcs.sceneData.lights.data[i];
|
||||
//
|
||||
// vec3 l = light.direction;
|
||||
// if (light.type != TYPE_DIRECTIONAL_LIGHT) {
|
||||
// l = normalize(light.position - fragPos);
|
||||
// }
|
||||
// float NoL = clamp(dot(n, l), 0.0, 1.0);
|
||||
//
|
||||
// float occlusion = 1.0;
|
||||
// if (light.type == TYPE_DIRECTIONAL_LIGHT) {
|
||||
// occlusion = calculateCSMOcclusion(
|
||||
// fragPos, cameraPos, NoL,
|
||||
// pcs.sceneData.csmShadowMapId,
|
||||
// pcs.sceneData.cascadeFarPlaneZs,
|
||||
// pcs.sceneData.csmLightSpaceTMs);
|
||||
// } else if (light.type == TYPE_POINT_LIGHT && light.shadowMapID != 0) {
|
||||
// occlusion = calculatePointShadow(
|
||||
// fragPos, light.position, NoL,
|
||||
// light.shadowMapID,
|
||||
// pcs.sceneData.pointLightFarPlane);
|
||||
// }
|
||||
//
|
||||
// fragColor += calculateLight(light, fragPos, n, v, l,
|
||||
// diffuseColor, roughness, metallic, f0, occlusion);
|
||||
// }
|
||||
//
|
||||
// // emissive
|
||||
// float emissiveF = material.metallicRoughnessEmissive.b;
|
||||
// vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
||||
// fragColor += emissiveColor;
|
||||
//
|
||||
// // ambient
|
||||
// fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||
//
|
||||
// #if 0
|
||||
// // CSM DEBUG
|
||||
// uint cascadeIndex = chooseCascade(inPos, cameraPos, pcs.sceneData.cascadeFarPlaneZs);
|
||||
// fragColor *= debugShadowsFactor(cascadeIndex);
|
||||
// #endif
|
||||
//
|
||||
// #if 0
|
||||
// // TANGENT DEBUG
|
||||
// if (inTangent == vec4(0.0)) {
|
||||
// fragColor = vec3(1.0f, 0.0f, 0.0f);
|
||||
// }
|
||||
// #endif
|
||||
//
|
||||
// #if 1
|
||||
// // NORMAL DEBUG
|
||||
// // fragColor = normal;
|
||||
// #endif
|
||||
|
||||
// outFragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);
|
||||
float metallicF = material.metallicRoughnessEmissive.r;
|
||||
float roughnessF = material.metallicRoughnessEmissive.g;
|
||||
|
||||
vec4 metallicRoughness = sampleTexture2DLinear(material.metallicRoughnessTex, inUV);
|
||||
|
||||
float roughness = roughnessF * metallicRoughness.g;
|
||||
// roughness *= roughness; // from perceptual to linear
|
||||
roughness = max(roughness, 1e-2); // 0.0 roughness leads to NaNs
|
||||
|
||||
float metallic = metallicF * metallicRoughness.b;
|
||||
|
||||
vec3 dielectricSpecular = vec3(0.04);
|
||||
vec3 black = vec3(0.0);
|
||||
vec3 diffuseColor = mix(baseColor * (1.0 - dielectricSpecular.r), black, metallic);
|
||||
vec3 f0 = mix(dielectricSpecular, baseColor, metallic);
|
||||
|
||||
vec3 cameraPos = pcs.sceneData.cameraPos.xyz;
|
||||
vec3 fragPos = inPos.xyz;
|
||||
vec3 n = normal;
|
||||
vec3 v = normalize(cameraPos - fragPos);
|
||||
|
||||
// forward+: only loop over the lights the culling pass put in this pixel's tile
|
||||
uvec2 tileID = uvec2(gl_FragCoord.xy) / LIGHT_TILE_SIZE;
|
||||
uint tileOffset = (tileID.y * pcs.sceneData.tileCountX + tileID.x) * LIGHT_TILE_STRIDE;
|
||||
uint tileLightCount = pcs.sceneData.tileLightIndices.data[tileOffset];
|
||||
|
||||
vec3 fragColor = vec3(0.0);
|
||||
for (uint i = 0; i < tileLightCount; i++) {
|
||||
uint lightIndex = pcs.sceneData.tileLightIndices.data[tileOffset + 1 + i];
|
||||
Light light = pcs.sceneData.lights.data[lightIndex];
|
||||
|
||||
fragColor += calculateLight(light, fragPos, n, v,
|
||||
diffuseColor, roughness, metallic, f0);
|
||||
}
|
||||
|
||||
// emissive
|
||||
float emissiveF = material.metallicRoughnessEmissive.b;
|
||||
vec3 emissiveColor = emissiveF * sampleTexture2DLinear(material.emissiveTex, inUV).rgb;
|
||||
fragColor += emissiveColor;
|
||||
|
||||
// ambient
|
||||
fragColor += baseColor * pcs.sceneData.ambientColor * pcs.sceneData.ambientIntensity;
|
||||
|
||||
if (pcs.sceneData.debugLightHeatmap != 0) {
|
||||
fragColor = mix(fragColor, lightHeatmapColor(tileLightCount), 0.5);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TANGENT DEBUG
|
||||
if (inTangent == vec4(0.0)) {
|
||||
fragColor = vec3(1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
// NORMAL DEBUG
|
||||
fragColor = normal;
|
||||
#endif
|
||||
|
||||
outFragColor = vec4(fragColor, diffuse.a);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ layout (location = 2) out vec3 outNormal;
|
||||
layout (location = 3) out vec4 outTangent;
|
||||
layout (location = 4) out mat3 outTBN;
|
||||
|
||||
// The depth pre-pass reuses this shader, positions have to match bit for bit
|
||||
// so the main pass can depth test against it with LESS_OR_EQUAL
|
||||
invariant gl_Position;
|
||||
|
||||
void main()
|
||||
{
|
||||
Vertex v = pcs.vertexBuffer.vertices[gl_VertexIndex];
|
||||
|
||||
@@ -6,11 +6,14 @@
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
#include "materials.glsl"
|
||||
#include "lights.glsl"
|
||||
|
||||
// Must match GameRenderer::GPUSceneData
|
||||
layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
||||
// camera
|
||||
mat4 view;
|
||||
mat4 proj;
|
||||
mat4 invProj;
|
||||
mat4 viewProj;
|
||||
vec4 cameraPos;
|
||||
|
||||
@@ -23,6 +26,14 @@ layout (buffer_reference, scalar) readonly buffer SceneDataBuffer {
|
||||
float fogDensity;
|
||||
|
||||
MaterialsBuffer materials;
|
||||
|
||||
// forward+
|
||||
LightsBuffer lights;
|
||||
TileLightIndicesBuffer tileLightIndices;
|
||||
uint numLights;
|
||||
uint tileCountX;
|
||||
uint debugLightHeatmap;
|
||||
uint padding;
|
||||
} sceneDataBuffer;
|
||||
|
||||
#endif // SCENE_DATA_GLSL
|
||||
|
||||
@@ -10,7 +10,6 @@ layout(location = 0) out vec4 outColor;
|
||||
layout(push_constant) uniform SkyboxPC {
|
||||
mat4 invViewProj;
|
||||
vec4 skyboxRot[3];
|
||||
vec3 cameraPos;
|
||||
uint skyboxTextureId;
|
||||
} pcs;
|
||||
|
||||
@@ -22,7 +21,8 @@ void main()
|
||||
vec4 world = pcs.invViewProj * ndc;
|
||||
vec3 worldPos = world.xyz / world.w;
|
||||
|
||||
vec3 dir = normalize(worldPos - pcs.cameraPos);
|
||||
// invViewProj has no translation, so the camera sits at the origin
|
||||
vec3 dir = normalize(worldPos);
|
||||
dir.y *= -1.0;
|
||||
|
||||
mat3 skyboxRot = mat3(
|
||||
|
||||
Reference in New Issue
Block a user