Add vk_ray_trace_indirect_scissor sample

This commit is contained in:
mklefrancois 2020-12-15 09:06:54 +01:00
parent 27d8a79ce3
commit a6690f149a
30 changed files with 4498 additions and 6 deletions

View file

@ -0,0 +1,18 @@
struct LanternIndirectEntry
{
// VkTraceRaysIndirectCommandKHR
int indirectWidth;
int indirectHeight;
int indirectDepth;
// Pixel coordinate of scissor rect upper-left.
int offsetX;
int offsetY;
// Lantern starts here:
// Can't use vec3 due to alignment.
float x, y, z;
float red, green, blue;
float brightness;
float radius;
};

View file

@ -0,0 +1,79 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_GOOGLE_include_directive : enable
#extension GL_EXT_scalar_block_layout : enable
#include "wavefront.glsl"
layout(push_constant) uniform shaderInformation
{
vec3 lightPosition;
uint instanceId;
float lightIntensity;
int lightType;
}
pushC;
// clang-format off
// Incoming
//layout(location = 0) flat in int matIndex;
layout(location = 1) in vec2 fragTexCoord;
layout(location = 2) in vec3 fragNormal;
layout(location = 3) in vec3 viewDir;
layout(location = 4) in vec3 worldPos;
// Outgoing
layout(location = 0) out vec4 outColor;
// Buffers
layout(binding = 1, scalar) buffer MatColorBufferObject { WaveFrontMaterial m[]; } materials[];
layout(binding = 2, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
layout(binding = 3) uniform sampler2D[] textureSamplers;
layout(binding = 4, scalar) buffer MatIndex { int i[]; } matIdx[];
// clang-format on
void main()
{
// Object of this instance
int objId = scnDesc.i[pushC.instanceId].objId;
// Material of the object
int matIndex = matIdx[nonuniformEXT(objId)].i[gl_PrimitiveID];
WaveFrontMaterial mat = materials[nonuniformEXT(objId)].m[matIndex];
vec3 N = normalize(fragNormal);
// Vector toward light
vec3 L;
float lightIntensity = pushC.lightIntensity;
if(pushC.lightType == 0)
{
vec3 lDir = pushC.lightPosition - worldPos;
float d = length(lDir);
lightIntensity = pushC.lightIntensity / (d * d);
L = normalize(lDir);
}
else
{
L = normalize(pushC.lightPosition - vec3(0));
}
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, N);
if(mat.textureId >= 0)
{
int txtOffset = scnDesc.i[pushC.instanceId].txtOffset;
uint txtId = txtOffset + mat.textureId;
vec3 diffuseTxt = texture(textureSamplers[nonuniformEXT(txtId)], fragTexCoord).xyz;
diffuse *= diffuseTxt;
}
// Specular
vec3 specular = computeSpecular(mat, viewDir, L, N);
// Result
outColor = vec4(lightIntensity * (diffuse + specular), 1);
}

View file

@ -0,0 +1,23 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
// Closest hit shader invoked when a primary ray hits a lantern.
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
// clang-format on
void main()
{
// Just look up this lantern's color. Self-illuminating, so no lighting calculations.
LanternIndirectEntry lantern = lanterns.lanterns[nonuniformEXT(gl_InstanceCustomIndexEXT)];
prd.hitValue = vec3(lantern.red, lantern.green, lantern.blue);
prd.additiveBlending = false;
}

View file

@ -0,0 +1,163 @@
#version 460
#extension GL_GOOGLE_include_directive : enable
// Compute shader for filling in raytrace indirect parameters for each lantern
// based on the current camera position (passed as view and proj matrix in
// push constant).
//
// Designed to be dispatched with only one work group; it alone fills in
// the entire lantern array (of length lanternCount, in also push constant).
#define LOCAL_SIZE 128
layout(local_size_x = LOCAL_SIZE, local_size_y = 1, local_size_z = 1) in;
#include "LanternIndirectEntry.glsl"
layout(binding = 0, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
layout(push_constant) uniform Constants
{
vec4 viewRowX;
vec4 viewRowY;
vec4 viewRowZ;
mat4 proj;
float nearZ;
int screenX;
int screenY;
int lanternCount;
}
pushC;
// Copy the technique of "2D Polyhedral Bounds of a Clipped,
// Perspective-Projected 3D Sphere" M. Mara M. McGuire
// http://jcgt.org/published/0002/02/05/paper.pdf
// to compute a screen-space rectangle covering the given Lantern's
// light radius-of-effect. Result is in screen (pixel) coordinates.
void getScreenCoordBox(in LanternIndirectEntry lantern, out ivec2 lower, out ivec2 upper);
// Use the xyz and radius of lanterns[i] plus the transformation matrices
// in pushC to fill in the offset and indirect parameters of lanterns[i]
// (defines the screen rectangle that this lantern's light is bounded in).
void fillIndirectEntry(int i)
{
LanternIndirectEntry lantern = lanterns.lanterns[i];
ivec2 lower, upper;
getScreenCoordBox(lantern, lower, upper);
lanterns.lanterns[i].indirectWidth = max(0, upper.x - lower.x);
lanterns.lanterns[i].indirectHeight = max(0, upper.y - lower.y);
lanterns.lanterns[i].indirectDepth = 1;
lanterns.lanterns[i].offsetX = lower.x;
lanterns.lanterns[i].offsetY = lower.y;
}
void main()
{
for (int i = int(gl_LocalInvocationID.x); i < pushC.lanternCount; i += LOCAL_SIZE)
{
fillIndirectEntry(i);
}
}
// Functions below modified from the paper.
float square(float a) { return a*a; }
void getBoundsForAxis(
in bool xAxis,
in vec3 center,
in float radius,
in float nearZ,
in mat4 projMatrix,
out vec3 U,
out vec3 L) {
bool trivialAccept = (center.z + radius) < nearZ; // Entirely in back of nearPlane (Trivial Accept)
vec3 a = xAxis ? vec3(1, 0, 0) : vec3(0, 1, 0);
// given in coordinates (a,z), where a is in the direction of the vector a, and z is in the standard z direction
vec2 projectedCenter = vec2(dot(a, center), center.z);
vec2 bounds_az[2];
float tSquared = dot(projectedCenter, projectedCenter) - square(radius);
float t, cLength, costheta = 0, sintheta = 0;
if(tSquared > 0) { // Camera is outside sphere
// Distance to the tangent points of the sphere (points where a vector from the camera are tangent to the sphere) (calculated a-z space)
t = sqrt(tSquared);
cLength = length(projectedCenter);
// Theta is the angle between the vector from the camera to the center of the sphere and the vectors from the camera to the tangent points
costheta = t / cLength;
sintheta = radius / cLength;
}
float sqrtPart = 0.0f;
if(!trivialAccept) sqrtPart = sqrt(square(radius) - square(nearZ - projectedCenter.y));
for(int i = 0; i < 2; ++i){
if(tSquared > 0) {
float x = costheta * projectedCenter.x + -sintheta * projectedCenter.y;
float y = sintheta * projectedCenter.x + costheta * projectedCenter.y;
bounds_az[i] = costheta * vec2(x, y);
}
if(!trivialAccept && (tSquared <= 0 || bounds_az[i].y > nearZ)) {
bounds_az[i].x = projectedCenter.x + sqrtPart;
bounds_az[i].y = nearZ;
}
sintheta *= -1; // negate theta for B
sqrtPart *= -1; // negate sqrtPart for B
}
U = bounds_az[0].x * a;
U.z = bounds_az[0].y;
L = bounds_az[1].x * a;
L.z = bounds_az[1].y;
}
/** Center is in camera space */
void getBoundingBox(
in vec3 center,
in float radius,
in float nearZ,
in mat4 projMatrix,
out vec2 ndc_low,
out vec2 ndc_high) {
vec3 maxXHomogenous, minXHomogenous, maxYHomogenous, minYHomogenous;
getBoundsForAxis(true, center, radius, nearZ, projMatrix, maxXHomogenous, minXHomogenous);
getBoundsForAxis(false, center, radius, nearZ, projMatrix, maxYHomogenous, minYHomogenous);
vec4 projRow0 = vec4(projMatrix[0][0], projMatrix[1][0], projMatrix[2][0], projMatrix[3][0]);
vec4 projRow1 = vec4(projMatrix[0][1], projMatrix[1][1], projMatrix[2][1], projMatrix[3][1]);
vec4 projRow3 = vec4(projMatrix[0][3], projMatrix[1][3], projMatrix[2][3], projMatrix[3][3]);
// We only need one coordinate for each point, so we save computation by only calculating x(or y) and w
float maxX_w = dot(vec4(maxXHomogenous, 1.0f), projRow3);
float minX_w = dot(vec4(minXHomogenous, 1.0f), projRow3);
float maxY_w = dot(vec4(maxYHomogenous, 1.0f), projRow3);
float minY_w = dot(vec4(minYHomogenous, 1.0f), projRow3);
float maxX = dot(vec4(maxXHomogenous, 1.0f), projRow0) / maxX_w;
float minX = dot(vec4(minXHomogenous, 1.0f), projRow0) / minX_w;
float maxY = dot(vec4(maxYHomogenous, 1.0f), projRow1) / maxY_w;
float minY = dot(vec4(minYHomogenous, 1.0f), projRow1) / minY_w;
// Paper minX, etc. names are misleading, not necessarily min. Fix here.
ndc_low = vec2(min(minX, maxX), min(minY, maxY));
ndc_high = vec2(max(minX, maxX), max(minY, maxY));
}
void getScreenCoordBox(in LanternIndirectEntry lantern, out ivec2 lower, out ivec2 upper)
{
vec4 lanternWorldCenter = vec4(lantern.x, lantern.y, lantern.z, 1);
vec3 center = vec3(
dot(pushC.viewRowX, lanternWorldCenter),
dot(pushC.viewRowY, lanternWorldCenter),
dot(pushC.viewRowZ, lanternWorldCenter));
vec2 ndc_low, ndc_high;
float paperNearZ = -abs(pushC.nearZ); // Paper expected negative nearZ, took 2 days to figure out!
getBoundingBox(center, lantern.radius, paperNearZ, pushC.proj, ndc_low, ndc_high);
// Convert NDC [-1,+1]^2 coordinates to screen coordinates, and clamp to stay in bounds.
lower.x = clamp(int((ndc_low.x * 0.5 + 0.5) * pushC.screenX), 0, pushC.screenX);
lower.y = clamp(int((ndc_low.y * 0.5 + 0.5) * pushC.screenY), 0, pushC.screenY);
upper.x = clamp(int((ndc_high.x * 0.5 + 0.5) * pushC.screenX), 0, pushC.screenX);
upper.y = clamp(int((ndc_high.y * 0.5 + 0.5) * pushC.screenY), 0, pushC.screenY);
}

View file

@ -0,0 +1,12 @@
#version 460
#extension GL_EXT_ray_tracing : require
// Miss shader invoked when tracing shadow rays (rays towards lantern)
// in lantern passes. Misses shouldn't really happen, but if they do,
// report we did not hit any lantern by setting hitLanternInstance = -1.
layout(location = 2) rayPayloadInEXT int hitLanternInstance;
void main()
{
hitLanternInstance = -1;
}

View file

@ -0,0 +1,18 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
// During a lantern pass, this closest hit shader is invoked when
// shadow rays (rays towards lantern) hit a lantern. Report back
// which lantern was hit.
// clang-format off
layout(location = 2) rayPayloadInEXT int hitLanternInstance;
// clang-format on
void main()
{
hitLanternInstance = gl_InstanceCustomIndexEXT;
}

View file

@ -0,0 +1,18 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
// During a lantern pass, this closest hit shader is invoked when
// shadow rays (rays towards lantern) hit a regular OBJ. Report back
// that no lantern was hit (-1).
// clang-format off
layout(location = 2) rayPayloadInEXT int hitLanternInstance;
// clang-format on
void main()
{
hitLanternInstance = -1;
}

View file

@ -0,0 +1,15 @@
#version 450
layout (location = 0) out vec2 outUV;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2);
gl_Position = vec4(outUV * 2.0f - 1.0f, 1.0f, 1.0f);
}

View file

@ -0,0 +1,18 @@
#version 450
layout(location = 0) in vec2 outUV;
layout(location = 0) out vec4 fragColor;
layout(set = 0, binding = 0) uniform sampler2D noisyTxt;
layout(push_constant) uniform shaderInformation
{
float aspectRatio;
}
pushc;
void main()
{
vec2 uv = outUV;
float gamma = 1. / 2.2;
fragColor = pow(texture(noisyTxt, uv).rgba, vec4(gamma));
}

View file

@ -0,0 +1,20 @@
#include "LanternIndirectEntry.glsl"
struct hitPayload
{
vec3 hitValue;
bool additiveBlending;
};
layout(push_constant) uniform Constants
{
vec4 clearColor;
vec3 lightPosition;
float lightIntensity;
int lightType; // 0: point, 1: infinite
int lanternPassNumber; // -1 if this is the full-screen pass. Otherwise, used to lookup trace indirect parameters.
int screenX;
int screenY;
int lanternDebug;
}
pushC;

View file

@ -0,0 +1,175 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_EXT_nonuniform_qualifier : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
#include "wavefront.glsl"
hitAttributeEXT vec2 attribs;
// clang-format off
layout(location = 0) rayPayloadInEXT hitPayload prd;
layout(location = 1) rayPayloadEXT bool isShadowed;
layout(location = 2) rayPayloadEXT int hitLanternInstance;
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
layout(binding = 1, set = 1, scalar) buffer MatColorBufferObject { WaveFrontMaterial m[]; } materials[];
layout(binding = 2, set = 1, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
layout(binding = 3, set = 1) uniform sampler2D textureSamplers[];
layout(binding = 4, set = 1) buffer MatIndexColorBuffer { int i[]; } matIndex[];
layout(binding = 5, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
layout(binding = 6, set = 1) buffer Indices { uint i[]; } indices[];
// clang-format on
void main()
{
// Object of this instance
uint objId = scnDesc.i[gl_InstanceCustomIndexEXT].objId;
// Indices of the triangle
ivec3 ind = ivec3(indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 0], //
indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 1], //
indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 2]); //
// Vertex of the triangle
Vertex v0 = vertices[nonuniformEXT(objId)].v[ind.x];
Vertex v1 = vertices[nonuniformEXT(objId)].v[ind.y];
Vertex v2 = vertices[nonuniformEXT(objId)].v[ind.z];
const vec3 barycentrics = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y);
// Computing the normal at hit position
vec3 normal = v0.nrm * barycentrics.x + v1.nrm * barycentrics.y + v2.nrm * barycentrics.z;
// Transforming the normal to world space
normal = normalize(vec3(scnDesc.i[gl_InstanceCustomIndexEXT].transfoIT * vec4(normal, 0.0)));
// Computing the coordinates of the hit position
vec3 worldPos = v0.pos * barycentrics.x + v1.pos * barycentrics.y + v2.pos * barycentrics.z;
// Transforming the position to world space
worldPos = vec3(scnDesc.i[gl_InstanceCustomIndexEXT].transfo * vec4(worldPos, 1.0));
// Vector toward the light
vec3 L;
vec3 colorIntensity = vec3(pushC.lightIntensity);
float lightDistance = 100000.0;
// ray direction is towards lantern, if in lantern pass.
if (pushC.lanternPassNumber >= 0)
{
LanternIndirectEntry lantern = lanterns.lanterns[pushC.lanternPassNumber];
vec3 lDir = vec3(lantern.x, lantern.y, lantern.z) - worldPos;
lightDistance = length(lDir);
vec3 color = vec3(lantern.red, lantern.green, lantern.blue);
// Lantern light decreases linearly. Not physically accurate, but looks good
// and avoids a hard "edge" at the radius limit. Use a constant value
// if lantern debug is enabled to clearly see the covered screen rectangle.
float distanceFade =
pushC.lanternDebug != 0
? 0.3
: max(0, (lantern.radius - lightDistance) / lantern.radius);
colorIntensity = color * lantern.brightness * distanceFade;
L = normalize(lDir);
}
// Non-lantern pass may have point light...
else if(pushC.lightType == 0)
{
vec3 lDir = pushC.lightPosition - worldPos;
lightDistance = length(lDir);
colorIntensity = vec3(pushC.lightIntensity / (lightDistance * lightDistance));
L = normalize(lDir);
}
else // or directional light.
{
L = normalize(pushC.lightPosition - vec3(0));
}
// Material of the object
int matIdx = matIndex[nonuniformEXT(objId)].i[gl_PrimitiveID];
WaveFrontMaterial mat = materials[nonuniformEXT(objId)].m[matIdx];
// Diffuse
vec3 diffuse = computeDiffuse(mat, L, normal);
if(mat.textureId >= 0)
{
uint txtId = mat.textureId + scnDesc.i[gl_InstanceCustomIndexEXT].txtOffset;
vec2 texCoord =
v0.texCoord * barycentrics.x + v1.texCoord * barycentrics.y + v2.texCoord * barycentrics.z;
diffuse *= texture(textureSamplers[nonuniformEXT(txtId)], texCoord).xyz;
}
vec3 specular = vec3(0);
float attenuation = 1;
// Tracing shadow ray only if the light is visible from the surface
if(dot(normal, L) > 0)
{
float tMin = 0.001;
float tMax = lightDistance;
vec3 origin = gl_WorldRayOriginEXT + gl_WorldRayDirectionEXT * gl_HitTEXT;
vec3 rayDir = L;
// Ordinary shadow from the simple tutorial.
if (pushC.lanternPassNumber < 0) {
isShadowed = true;
uint flags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT
| gl_RayFlagsSkipClosestHitShaderEXT;
traceRayEXT(topLevelAS, // acceleration structure
flags, // rayFlags
0xFF, // cullMask
0, // sbtRecordOffset
0, // sbtRecordStride
1, // missIndex
origin, // ray origin
tMin, // ray min range
rayDir, // ray direction
tMax, // ray max range
1 // payload (location = 1)
);
}
// Lantern shadow ray. Cast a ray towards the lantern whose lighting is being
// added this pass. Only the closest hit shader for lanterns will set
// hitLanternInstance (payload 2) to non-negative value.
else {
// Skip ray if no light would be added anyway.
if (colorIntensity == vec3(0)) {
isShadowed = true;
}
else {
uint flags = gl_RayFlagsOpaqueEXT;
hitLanternInstance = -1;
traceRayEXT(topLevelAS, // acceleration structure
flags, // rayFlags
0xFF, // cullMask
2, // sbtRecordOffset : lantern shadow hit groups start at index 2.
0, // sbtRecordStride
2, // missIndex : lantern shadow miss shader is number 2.
origin, // ray origin
tMin, // ray min range
rayDir, // ray direction
tMax, // ray max range
2 // payload (location = 2)
);
// Did we hit the lantern we expected?
isShadowed = (hitLanternInstance != pushC.lanternPassNumber);
}
}
if(isShadowed)
{
attenuation = 0.1;
}
else
{
// Specular
specular = computeSpecular(mat, gl_WorldRayDirectionEXT, L, normal);
}
}
prd.hitValue = colorIntensity * (attenuation * (diffuse + specular));
prd.additiveBlending = true;
}

View file

@ -0,0 +1,71 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
layout(binding = 1, set = 0, rgba32f) uniform image2D image;
layout(location = 0) rayPayloadEXT hitPayload prd;
layout(binding = 0, set = 1) uniform CameraProperties
{
mat4 view;
mat4 proj;
mat4 viewInverse;
mat4 projInverse;
}
cam;
layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
void main()
{
// Global light pass is a full screen rectangle (lower corner 0,0), but
// lantern passes are only run within rectangles that may be offset.
ivec2 pixelOffset = ivec2(0);
if (pushC.lanternPassNumber >= 0)
{
pixelOffset.x = lanterns.lanterns[pushC.lanternPassNumber].offsetX;
pixelOffset.y = lanterns.lanterns[pushC.lanternPassNumber].offsetY;
}
const ivec2 pixelIntCoord = ivec2(gl_LaunchIDEXT.xy) + pixelOffset;
const vec2 pixelCenter = vec2(pixelIntCoord) + vec2(0.5);
const vec2 inUV = pixelCenter / vec2(pushC.screenX, pushC.screenY);
vec2 d = inUV * 2.0 - 1.0;
vec4 origin = cam.viewInverse * vec4(0, 0, 0, 1);
vec4 target = cam.projInverse * vec4(d.x, d.y, 1, 1);
vec4 direction = cam.viewInverse * vec4(normalize(target.xyz), 0);
uint rayFlags = gl_RayFlagsOpaqueEXT;
float tMin = 0.001;
float tMax = 10000.0;
// Lanterns (self-illuminating) and miss shader (constant background color)
// do not use additive blending. Only normal OBJ geometry is additive,
// OBJ closest hit sets this to true.
prd.additiveBlending = false;
traceRayEXT(topLevelAS, // acceleration structure
rayFlags, // rayFlags
0xFF, // cullMask
0, // sbtRecordOffset
0, // sbtRecordStride
0, // missIndex
origin.xyz, // ray origin
tMin, // ray min range
direction.xyz, // ray direction
tMax, // ray max range
0 // payload (location = 0)
);
// Either add to or replace output image color based on prd.additiveBlending.
// Global pass always replaces color as it is the first pass.
vec3 oldColor = vec3(0);
if (prd.additiveBlending && pushC.lanternPassNumber >= 0) {
oldColor = imageLoad(image, pixelIntCoord).rgb;
}
imageStore(image, pixelIntCoord, vec4(prd.hitValue + oldColor, 1.0));
}

View file

@ -0,0 +1,12 @@
#version 460
#extension GL_EXT_ray_tracing : require
#extension GL_GOOGLE_include_directive : enable
#include "raycommon.glsl"
layout(location = 0) rayPayloadInEXT hitPayload prd;
void main()
{
prd.hitValue = pushC.clearColor.xyz * 0.8;
prd.additiveBlending = false;
}

View file

@ -0,0 +1,9 @@
#version 460
#extension GL_EXT_ray_tracing : require
layout(location = 1) rayPayloadInEXT bool isShadowed;
void main()
{
isShadowed = false;
}

View file

@ -0,0 +1,61 @@
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_scalar_block_layout : enable
#extension GL_GOOGLE_include_directive : enable
#include "wavefront.glsl"
// clang-format off
layout(binding = 2, set = 0, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
// clang-format on
layout(binding = 0) uniform UniformBufferObject
{
mat4 view;
mat4 proj;
mat4 viewI;
}
ubo;
layout(push_constant) uniform shaderInformation
{
vec3 lightPosition;
uint instanceId;
float lightIntensity;
int lightType;
}
pushC;
layout(location = 0) in vec3 inPosition;
layout(location = 1) in vec3 inNormal;
layout(location = 2) in vec3 inColor;
layout(location = 3) in vec2 inTexCoord;
//layout(location = 0) flat out int matIndex;
layout(location = 1) out vec2 fragTexCoord;
layout(location = 2) out vec3 fragNormal;
layout(location = 3) out vec3 viewDir;
layout(location = 4) out vec3 worldPos;
out gl_PerVertex
{
vec4 gl_Position;
};
void main()
{
mat4 objMatrix = scnDesc.i[pushC.instanceId].transfo;
mat4 objMatrixIT = scnDesc.i[pushC.instanceId].transfoIT;
vec3 origin = vec3(ubo.viewI * vec4(0, 0, 0, 1));
worldPos = vec3(objMatrix * vec4(inPosition, 1.0));
viewDir = vec3(worldPos - origin);
fragTexCoord = inTexCoord;
fragNormal = vec3(objMatrixIT * vec4(inNormal, 0.0));
// matIndex = inMatID;
gl_Position = ubo.proj * ubo.view * vec4(worldPos, 1.0);
}

View file

@ -0,0 +1,58 @@
struct Vertex
{
vec3 pos;
vec3 nrm;
vec3 color;
vec2 texCoord;
};
struct WaveFrontMaterial
{
vec3 ambient;
vec3 diffuse;
vec3 specular;
vec3 transmittance;
vec3 emission;
float shininess;
float ior; // index of refraction
float dissolve; // 1 == opaque; 0 == fully transparent
int illum; // illumination model (see http://www.fileformat.info/format/material/)
int textureId;
};
struct sceneDesc
{
int objId;
int txtOffset;
mat4 transfo;
mat4 transfoIT;
};
vec3 computeDiffuse(WaveFrontMaterial mat, vec3 lightDir, vec3 normal)
{
// Lambertian
float dotNL = max(dot(normal, lightDir), 0.0);
vec3 c = mat.diffuse * dotNL;
if(mat.illum >= 1)
c += mat.ambient;
return c;
}
vec3 computeSpecular(WaveFrontMaterial mat, vec3 viewDir, vec3 lightDir, vec3 normal)
{
if(mat.illum < 2)
return vec3(0);
// Compute specular only if not in shadow
const float kPi = 3.14159265;
const float kShininess = max(mat.shininess, 4.0);
// Specular
const float kEnergyConservation = (2.0 + kShininess) / (2.0 * kPi);
vec3 V = normalize(-viewDir);
vec3 R = reflect(-lightDir, normal);
float specular = kEnergyConservation * pow(max(dot(V, R), 0.0), kShininess);
return vec3(mat.specular * specular);
}