Redo of render paths
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
|
||||
import LightEnv;
|
||||
import Common;
|
||||
|
||||
struct ComputeShaderInput
|
||||
{
|
||||
uint3 groupID : SV_GroupID;
|
||||
uint3 groupThreadID : SV_GroupThreadID;
|
||||
uint3 dispatchThreadID : SV_DispatchThreadID;
|
||||
uint groupIndex : SV_GroupIndex;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
cbuffer DispatchParams
|
||||
{
|
||||
uint3 numThreadGroups;
|
||||
uint pad0;
|
||||
uint3 numThreads;
|
||||
uint pad1;
|
||||
}
|
||||
layout(set = 0, binding = 2)
|
||||
RWStructuredBuffer<Frustum> out_Frustums;
|
||||
|
||||
|
||||
[numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)]
|
||||
[shader("compute")]
|
||||
void computeFrustums(ComputeShaderInput in)
|
||||
{
|
||||
const float3 eyePos = float3(0,0,0);
|
||||
|
||||
float4 screenSpace[4];
|
||||
|
||||
screenSpace[0] = float4(in.dispatchThreadID.xy * BLOCK_SIZE, 1.0f, 1.0f);
|
||||
screenSpace[1] = float4(float2(in.dispatchThreadID.x + 1, in.dispatchThreadID.y) * BLOCK_SIZE, 1.0f, 1.0f);
|
||||
screenSpace[2] = float4(float2(in.dispatchThreadID.x, in.dispatchThreadID.y + 1) * BLOCK_SIZE, 1.0f, 1.0f);
|
||||
screenSpace[3] = float4(float2(in.dispatchThreadID.x + 1, in.dispatchThreadID.y + 1) * BLOCK_SIZE, 1.0f, 1.0f);
|
||||
|
||||
//Convert to viewSpace
|
||||
float3 viewSpace[4];
|
||||
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
viewSpace[i] = screenToView(screenSpace[i]).xyz;
|
||||
}
|
||||
|
||||
//Compute frustum
|
||||
Frustum frustum;
|
||||
|
||||
frustum.planes[0] = computePlane(eyePos, viewSpace[0], viewSpace[2]);
|
||||
frustum.planes[1] = computePlane(eyePos, viewSpace[3], viewSpace[1]);
|
||||
frustum.planes[2] = computePlane(eyePos, viewSpace[1], viewSpace[0]);
|
||||
frustum.planes[3] = computePlane(eyePos, viewSpace[2], viewSpace[3]);
|
||||
|
||||
if(in.dispatchThreadID.x < numThreads.x && in.dispatchThreadID.y < numThreads.y)
|
||||
{
|
||||
uint index = in.dispatchThreadID.x + (in.dispatchThreadID.y * numThreads.x);
|
||||
out_Frustums[index] = frustum;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
import InputGeometry;
|
||||
|
||||
struct ViewParams
|
||||
{
|
||||
float4x4 viewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
float4 cameraPos_WS;
|
||||
};
|
||||
|
||||
layout(set = 0, binding = 0)
|
||||
ParameterBlock<ViewParams> gViewParams;
|
||||
|
||||
struct ModelParameter
|
||||
{
|
||||
float4x4 modelMatrix;
|
||||
}
|
||||
|
||||
[[vk::push_constant]]
|
||||
ConstantBuffer<ModelParameter> gModelParams;
|
||||
|
||||
struct VertexShaderOutput
|
||||
{
|
||||
float4 out_Position : SV_Position;
|
||||
}
|
||||
[shader("vertex")]
|
||||
VertexShaderOutput depthPrepass(PositionOnlyVertexInput input)
|
||||
{
|
||||
VertexShaderOutput out;
|
||||
float4 worldPos = mul(gModelParams.modelMatrix, float4(input.getVertexPosition(), 1));
|
||||
float4 viewPos = mul(gViewParams.viewMatrix, worldPos);
|
||||
out.out_Position = mul(gViewParams.projectionMatrix, viewPos);
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import LightEnv;
|
||||
|
||||
struct VertexInput
|
||||
{
|
||||
float3 basePosition;
|
||||
float3 position;
|
||||
float3 velocity;
|
||||
}
|
||||
|
||||
struct ViewParameter
|
||||
{
|
||||
float4 cameraPos_WS;
|
||||
float4x4 viewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
PointLight light;
|
||||
}
|
||||
layout(set = 0, std430)
|
||||
ParameterBlock<ViewParameter> gViewParams;
|
||||
|
||||
struct VertexToPixel
|
||||
{
|
||||
float4 position_CS : SV_Position;
|
||||
}
|
||||
|
||||
VertexToPixel vertexMain(VertexInput input)
|
||||
{
|
||||
VertexToPixel output = (VertexToPixel)0;
|
||||
float3 cameraRight_WS = float3(gViewParams.viewMatrix[0][0], gViewParams.viewMatrix[0][1], gViewParams.viewMatrix[0][2]);
|
||||
float3 cameraUp_WS = float3(gViewParams.viewMatrix[1][0], gViewParams.viewMatrix[1][1], gViewParams.viewMatrix[1][2]);
|
||||
float3 worldPosition = input.position + cameraRight_WS * input.basePosition.x + cameraUp_WS * input.basePosition.y;
|
||||
float4 viewPosition = mul(gViewParams.viewMatrix, float4(worldPosition, 1));
|
||||
output.position_CS = mul(gViewParams.projectionMatrix, viewPosition);
|
||||
return output;
|
||||
}
|
||||
|
||||
float4 fragMain(VertexToPixel input) : SV_Target
|
||||
{
|
||||
return float4(0, 1, 0, 0.001f);
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import InputGeometry;
|
||||
import LightEnv;
|
||||
import BRDF;
|
||||
import Material;
|
||||
import TexturedMaterial;
|
||||
import FlatColorMaterial;
|
||||
|
||||
struct ViewParameter
|
||||
{
|
||||
float4x4 viewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
float4 cameraPos_WS;
|
||||
}
|
||||
layout(set = 0, binding = 0, std430)
|
||||
ConstantBuffer<ViewParameter> gViewParams;
|
||||
|
||||
layout(set = 0, binding = 1, std430)
|
||||
ConstantBuffer<Lights> gLightEnv;
|
||||
|
||||
layout(set = 1, std430)
|
||||
type_param TMaterial : IMaterial;
|
||||
ParameterBlock<TMaterial> gMaterial;
|
||||
|
||||
struct ModelParameter
|
||||
{
|
||||
float4x4 modelMatrix;
|
||||
}
|
||||
|
||||
[[vk::push_constant]]
|
||||
ConstantBuffer<ModelParameter> gModelParams;
|
||||
|
||||
struct VertexStageOutput
|
||||
{
|
||||
MaterialPixelParameter materialParameter : MaterialParameter;
|
||||
float4 sv_position : SV_Position;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
VertexStageOutput vertexMain(
|
||||
InputGeometry inputGeometry)
|
||||
{
|
||||
VertexStageOutput output;
|
||||
MaterialPixelParameter pixelParams;
|
||||
float4 worldPosition = mul(gModelParams.modelMatrix, float4(inputGeometry.getVertexPosition(), 1));
|
||||
pixelParams.position = worldPosition.xyz;
|
||||
pixelParams.texCoord = inputGeometry.texCoord;
|
||||
|
||||
float4 viewPosition = mul(gViewParams.viewMatrix, worldPosition);
|
||||
pixelParams.viewDir = gViewParams.cameraPos_WS.xyz - worldPosition.xyz;
|
||||
|
||||
pixelParams.normal = mul(gModelParams.modelMatrix, float4(inputGeometry.getNormal(), 0)).xyz;
|
||||
pixelParams.tangent = mul(gModelParams.modelMatrix, float4(inputGeometry.getTangent(), 0)).xyz;
|
||||
pixelParams.biTangent = mul(gModelParams.modelMatrix, float4(inputGeometry.getBiTangent(), 0)).xyz;
|
||||
pixelParams.clipPosition = mul(gViewParams.projectionMatrix, viewPosition);
|
||||
|
||||
output.materialParameter = pixelParams;
|
||||
output.sv_position = pixelParams.clipPosition;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 fragmentMain(MaterialPixelParameter input : MaterialParameter) : SV_Target
|
||||
{
|
||||
TMaterial.BRDF brdf = gMaterial.prepare(input);
|
||||
|
||||
float3 viewDir = normalize(input.viewDir);
|
||||
|
||||
float3 result = float3(0, 0, 0);
|
||||
for (int i = 0; i < gLightEnv.numDirectionalLights; ++i)
|
||||
{
|
||||
result += gLightEnv.directionalLights[i].illuminate(input, brdf, viewDir);
|
||||
}
|
||||
for (int i = 0; i < gLightEnv.numPointLights; ++i)
|
||||
{
|
||||
result += gLightEnv.pointLights[i].illuminate(input, brdf, viewDir);
|
||||
}
|
||||
|
||||
return float4(result, 0);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import InputGeometry;
|
||||
import LightEnv;
|
||||
import BRDF;
|
||||
import Material;
|
||||
import TexturedMaterial;
|
||||
import FlatColorMaterial;
|
||||
import Common;
|
||||
|
||||
struct ViewParameter
|
||||
{
|
||||
float4x4 viewMatrix;
|
||||
float4x4 projectionMatrix;
|
||||
float4 cameraPos_WS;
|
||||
}
|
||||
layout(set = 0, binding = 0, std430)
|
||||
ConstantBuffer<ViewParameter> gViewParams;
|
||||
|
||||
layout(set = 0, binding = 1, std430)
|
||||
ConstantBuffer<Lights> gLightEnv;
|
||||
layout(set = 0, binding = 2)
|
||||
StructuredBuffer<uint> lightIndexList;
|
||||
layout(set = 0, binding = 3)
|
||||
RWTexture2D<uint2> lightGrid;
|
||||
|
||||
layout(set = 1, std430)
|
||||
type_param TMaterial : IMaterial;
|
||||
ParameterBlock<TMaterial> gMaterial;
|
||||
|
||||
struct ModelParameter
|
||||
{
|
||||
float4x4 modelMatrix;
|
||||
}
|
||||
|
||||
layout(set = 2)
|
||||
ConstantBuffer<ModelParameter> gModelParams;
|
||||
|
||||
struct VertexStageOutput
|
||||
{
|
||||
MaterialPixelParameter materialParameter : MaterialParameter;
|
||||
float4 sv_position : SV_Position;
|
||||
};
|
||||
|
||||
[shader("vertex")]
|
||||
VertexStageOutput vertexMain(
|
||||
InputGeometry inputGeometry)
|
||||
{
|
||||
VertexStageOutput output;
|
||||
MaterialPixelParameter pixelParams;
|
||||
float4 worldPosition = mul(gModelParams.modelMatrix, float4(inputGeometry.getVertexPosition(), 1));
|
||||
pixelParams.position = worldPosition.xyz;
|
||||
pixelParams.texCoord = inputGeometry.texCoord;
|
||||
|
||||
float4 viewPosition = mul(gViewParams.viewMatrix, worldPosition);
|
||||
pixelParams.viewDir = gViewParams.cameraPos_WS.xyz - worldPosition.xyz;
|
||||
|
||||
pixelParams.normal = mul(gModelParams.modelMatrix, float4(inputGeometry.getNormal(), 0)).xyz;
|
||||
pixelParams.tangent = mul(gModelParams.modelMatrix, float4(inputGeometry.getTangent(), 0)).xyz;
|
||||
pixelParams.biTangent = mul(gModelParams.modelMatrix, float4(inputGeometry.getBiTangent(), 0)).xyz;
|
||||
pixelParams.clipPosition = mul(gViewParams.projectionMatrix, viewPosition);
|
||||
|
||||
output.materialParameter = pixelParams;
|
||||
output.sv_position = pixelParams.clipPosition;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
[shader("fragment")]
|
||||
float4 fragmentMain(MaterialPixelParameter input : MaterialParameter) : SV_Target
|
||||
{
|
||||
TMaterial.BRDF brdf = gMaterial.prepare(input);
|
||||
|
||||
float3 viewDir = normalize(input.viewDir);
|
||||
|
||||
float3 result = float3(0, 0, 0);
|
||||
for (int i = 0; i < gLightEnv.numDirectionalLights; ++i)
|
||||
{
|
||||
result += gLightEnv.directionalLights[i].illuminate(input, brdf, viewDir);
|
||||
}
|
||||
uint2 tileIndex = uint2(floor(input.clipPosition.xy) / BLOCK_SIZE);
|
||||
|
||||
uint startOffset = lightGrid[tileIndex].x;
|
||||
uint lightCount = lightGrid[tileIndex].y;
|
||||
|
||||
for (int j = 0; j < lightCount; ++j)
|
||||
{
|
||||
uint lightIndex = lightIndexList[startOffset + j];
|
||||
PointLight pointLight = gLightEnv.pointLights[lightIndex];
|
||||
result += pointLight.illuminate(input, brdf, viewDir);
|
||||
}
|
||||
|
||||
return float4(result, 1);
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import LightEnv;
|
||||
import Common;
|
||||
|
||||
struct ComputeShaderInput
|
||||
{
|
||||
uint3 groupID : SV_GroupID;
|
||||
uint3 groupThreadID : SV_GroupThreadID;
|
||||
uint3 dispatchThreadID : SV_DispatchThreadID;
|
||||
uint groupIndex : SV_GroupIndex;
|
||||
};
|
||||
layout(binding = 0)
|
||||
cbuffer DispatchParams
|
||||
{
|
||||
uint3 numThreadGroups;
|
||||
uint pad0;
|
||||
uint3 numThreads;
|
||||
uint pad1;
|
||||
}
|
||||
|
||||
layout(binding = 2)
|
||||
RWTexture2D depthTextureVS;
|
||||
layout(binding = 3)
|
||||
ConstantBuffer<Lights> lights;
|
||||
layout(binding = 4)
|
||||
StructuredBuffer<Frustum> frustums;
|
||||
|
||||
layout(binding = 5)
|
||||
RWStructuredBuffer<uint> oLightIndexCounter;
|
||||
layout(binding = 6)
|
||||
RWStructuredBuffer<uint> tLightIndexCounter;
|
||||
|
||||
layout(binding = 7)
|
||||
RWStructuredBuffer<uint> oLightIndexList;
|
||||
layout(binding = 8)
|
||||
RWStructuredBuffer<uint> tLightIndexList;
|
||||
layout(binding = 9)
|
||||
RWTexture2D<uint2> oLightGrid;
|
||||
layout(binding = 10)
|
||||
RWTexture2D<uint2> tLightGrid;
|
||||
|
||||
groupshared uint uMinDepth;
|
||||
groupshared uint uMaxDepth;
|
||||
|
||||
groupshared Frustum groupFrustum;
|
||||
|
||||
groupshared uint oLightCount;
|
||||
groupshared uint oLightIndexStartOffset;
|
||||
groupshared uint oLightList[1024];
|
||||
|
||||
groupshared uint tLightCount;
|
||||
groupshared uint tLightIndexStartOffset;
|
||||
groupshared uint tLightList[1024];
|
||||
|
||||
|
||||
void oAppendLight(uint lightIndex)
|
||||
{
|
||||
uint index;
|
||||
InterlockedAdd(oLightCount, 1, index);
|
||||
if(index < 1024)
|
||||
{
|
||||
oLightList[index] = lightIndex;
|
||||
}
|
||||
}
|
||||
|
||||
void tAppendLight(uint lightIndex)
|
||||
{
|
||||
uint index;
|
||||
InterlockedAdd(tLightCount, 1, index);
|
||||
if(index < 1024)
|
||||
{
|
||||
tLightList[index] = lightIndex;
|
||||
}
|
||||
}
|
||||
|
||||
[numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)]
|
||||
void cullLights(ComputeShaderInput in)
|
||||
{
|
||||
int2 texCoord = in.dispatchThreadID.xy;
|
||||
float fDepth = depthTextureVS.Load(texCoord).r;
|
||||
|
||||
uint uDepth = asuint(fDepth);
|
||||
if(in.groupIndex == 0)
|
||||
{
|
||||
uMinDepth = 0xffffffff;
|
||||
uMaxDepth = 0x0;
|
||||
oLightCount = 0;
|
||||
tLightCount = 0;
|
||||
groupFrustum = frustums[in.groupID.x + (in.groupID.y * numThreadGroups.x)];
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
InterlockedMin(uMinDepth, uDepth);
|
||||
InterlockedMax(uMaxDepth, uDepth);
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
float fMinDepth = asfloat(uMinDepth);
|
||||
float fMaxDepth = asfloat(uMaxDepth);
|
||||
|
||||
float minDepthVS = clipToView(float4(0, 0, fMinDepth, 1)).z;
|
||||
float maxDepthVS = clipToView(float4(0, 0, fMaxDepth, 1)).z;
|
||||
float nearClipVS = clipToView(float4(0, 0, 0, 1.0f)).z;
|
||||
|
||||
Plane minPlane = {float3(0, 0, -1), -minDepthVS};
|
||||
|
||||
for ( uint i = in.groupIndex; i < lights.numPointLights; i += BLOCK_SIZE * BLOCK_SIZE )
|
||||
{
|
||||
PointLight light = lights.pointLights[i];
|
||||
//if(light.insideFrustum(groupFrustum, nearClipVS, maxDepthVS))
|
||||
{
|
||||
//InterlockedAdd(tLightCount, 1, index);
|
||||
//if(index < 1024)
|
||||
//{
|
||||
// tLightList[index] = i;
|
||||
//}
|
||||
//if(!light.insidePlane(minPlane))
|
||||
//{
|
||||
oAppendLight(i);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
if(in.groupIndex == 0)
|
||||
{
|
||||
InterlockedAdd(oLightIndexCounter[0], (uint)oLightCount, oLightIndexStartOffset);
|
||||
oLightGrid[in.groupID.xy] = uint2(oLightIndexStartOffset, oLightCount);
|
||||
|
||||
InterlockedAdd(tLightIndexCounter[0], (uint)tLightCount, tLightIndexStartOffset);
|
||||
tLightGrid[in.groupID.xy] = uint2(tLightIndexStartOffset, tLightCount);
|
||||
}
|
||||
GroupMemoryBarrierWithGroupSync();
|
||||
|
||||
if(in.groupIndex == 0)
|
||||
{
|
||||
for (uint j = 0; j < (uint)oLightCount; j += 1/*BLOCK_SIZE * BLOCK_SIZE*/)
|
||||
{
|
||||
oLightIndexList[oLightIndexStartOffset + j] = oLightList[j];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// For transparent geometry.
|
||||
for ( uint k = in.groupIndex; k < (uint)tLightCount; k += BLOCK_SIZE * BLOCK_SIZE )
|
||||
{
|
||||
tLightIndexList[tLightIndexStartOffset + k] = tLightList[k];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "Placeholder",
|
||||
"profile": "BlinnPhong",
|
||||
"params": {
|
||||
"diffuseTexture": {
|
||||
"type": "Texture2D"
|
||||
},
|
||||
"specularTexture": {
|
||||
"type": "Texture2D"
|
||||
},
|
||||
"normalTexture": {
|
||||
"type": "Texture2D"
|
||||
},
|
||||
"uvScale": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"metallic": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"subsurface": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"roughness": {
|
||||
"type": "float",
|
||||
"default": "0.5f"
|
||||
},
|
||||
"specularTint": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"anisotropic": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"sheen": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"sheenTint": {
|
||||
"type": "float",
|
||||
"default": "0.5f"
|
||||
},
|
||||
"clearCoat": {
|
||||
"type": "float",
|
||||
"default": "0.0f"
|
||||
},
|
||||
"clearCoatGloss": {
|
||||
"type": "float",
|
||||
"default": "1.0f"
|
||||
},
|
||||
"textureSampler": {
|
||||
"type": "SamplerState"
|
||||
}
|
||||
},
|
||||
"code": [
|
||||
"result.baseColor = diffuseTexture.Sample(textureSampler, geometry.texCoord * uvScale).xyz;",
|
||||
"result.metallic = 0;",
|
||||
"float3 bumpMapNormal = normalTexture.Sample(textureSampler, geometry.texCoord * uvScale).xyz;",
|
||||
"bumpMapNormal = 2.0 * bumpMapNormal - float3(1.0, 1.0, 1.0);",
|
||||
"result.normal = geometry.transformLocalToWorld(bumpMapNormal);",
|
||||
"result.specular = specularTexture.Sample(textureSampler, geometry.texCoord * uvScale).x;",
|
||||
"result.roughness = roughness;",
|
||||
"result.specularTint = specularTint;",
|
||||
"result.anisotropic = anisotropic;",
|
||||
"result.sheen = sheen;",
|
||||
"result.sheenTint = sheenTint;",
|
||||
"result.clearCoat = clearCoat;",
|
||||
"result.clearCoatGloss = clearCoatGloss;",
|
||||
"return result;"
|
||||
]
|
||||
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import Common;
|
||||
|
||||
interface IBRDF
|
||||
{
|
||||
float3 evaluate(float3 view, float3 light, float3 normal, float3 tangent, float3 biTangent, float3 lightColor);
|
||||
};
|
||||
|
||||
struct BlinnPhong : IBRDF
|
||||
{
|
||||
float3 baseColor;
|
||||
float metallic = 0;
|
||||
float3 normal = float3(0, 1, 0);
|
||||
float subsurface = 0;
|
||||
float specular = 0.5;
|
||||
float roughness = 0.5;
|
||||
float specularTint = 0;
|
||||
float anisotropic = 0;
|
||||
float sheen = 0;
|
||||
float sheenTint = 0.5f;
|
||||
float clearCoat = 0;
|
||||
float clearCoatGloss = 1;
|
||||
|
||||
float3 evaluate(float3 view, float3 light, float3 surfaceNormal, float3 tangent, float3 biTangent, float3 lightColor)
|
||||
{
|
||||
float nDotL = saturate(dot(normal, light));
|
||||
float3 h = normalize(light + view);
|
||||
float nDotH = saturate(dot(normal, h));
|
||||
|
||||
return baseColor * nDotL + lightColor * specular * pow(nDotH, sheen);
|
||||
}
|
||||
};
|
||||
|
||||
struct DisneyBRDF : IBRDF
|
||||
{
|
||||
float3 baseColor;
|
||||
float metallic = 0;
|
||||
float3 normal = float3(0, 1, 0);
|
||||
float subsurface = 0;
|
||||
float specular = 0.5;
|
||||
float roughness = 0.5;
|
||||
float specularTint = 0;
|
||||
float anisotropic = 0;
|
||||
float sheen = 0;
|
||||
float sheenTint = 0.5f;
|
||||
float clearCoat = 0;
|
||||
float clearCoatGloss = 1;
|
||||
|
||||
float sqr(float x)
|
||||
{
|
||||
return x * x;
|
||||
}
|
||||
|
||||
float SchlickFresnel(float u)
|
||||
{
|
||||
float m = clamp(1 - u, 0, 1);
|
||||
float m2 = m * m;
|
||||
return m2 * m2 * m; // pow(m,5)
|
||||
}
|
||||
|
||||
float GTR1(float NdotH, float a)
|
||||
{
|
||||
if (a >= 1)
|
||||
return 1 / PI;
|
||||
float a2 = a * a;
|
||||
float t = 1 + (a2 - 1) * NdotH * NdotH;
|
||||
return (a2 - 1) / (PI * log(a2) * t);
|
||||
}
|
||||
|
||||
float GTR2(float NdotH, float a)
|
||||
{
|
||||
float a2 = a * a;
|
||||
float t = 1 + (a2 - 1) * NdotH * NdotH;
|
||||
return a2 / (PI * t * t);
|
||||
}
|
||||
|
||||
float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
|
||||
{
|
||||
return 1 / (PI * ax * ay * sqr(sqr(HdotX / ax) + sqr(HdotY / ay) + NdotH * NdotH));
|
||||
}
|
||||
|
||||
float smithG_GGX(float NdotV, float alphaG)
|
||||
{
|
||||
float a = alphaG * alphaG;
|
||||
float b = NdotV * NdotV;
|
||||
return 1 / (NdotV + sqrt(a + b - a * b));
|
||||
}
|
||||
|
||||
float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
|
||||
{
|
||||
return 1 / (NdotV + sqrt(sqr(VdotX * ax) + sqr(VdotY * ay) + sqr(NdotV)));
|
||||
}
|
||||
|
||||
float3 mon2lin(float3 x)
|
||||
{
|
||||
return float3(pow(x[0], 2.2), pow(x[1], 2.2), pow(x[2], 2.2));
|
||||
}
|
||||
|
||||
|
||||
float3 evaluate(float3 V, float3 L, float3 surfaceNormal, float3 X, float3 Y, float3 lightColor)
|
||||
{
|
||||
float3 N = normal;
|
||||
float NdotL = dot(N, L);
|
||||
float NdotV = dot(N, V);
|
||||
if (NdotL < 0 || NdotV < 0)
|
||||
return float3(0);
|
||||
float3 H = normalize(L + V);
|
||||
|
||||
float NdotH = dot(N, H);
|
||||
float LdotH = dot(L, H);
|
||||
float3 Cdlin = mon2lin(baseColor);
|
||||
float Cdlum = .3 * Cdlin[0] + .6 * Cdlin[1] + .1 * Cdlin[2]; // luminance approx.
|
||||
float3 Ctint = Cdlum > 0 ? Cdlin / Cdlum : float3(1); // normalize lum. to isolate hue+sat
|
||||
float3 Cspec0 = lerp(specular * .08 * lerp(float3(1), Ctint, specularTint), Cdlin, metallic);
|
||||
float3 Csheen = lerp(float3(1), Ctint, sheenTint);
|
||||
|
||||
// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
|
||||
// and mix in diffuse retro-reflection based on roughness
|
||||
float FL = SchlickFresnel(NdotL), FV = SchlickFresnel(NdotV);
|
||||
float Fd90 = 0.5 + 2 * LdotH * LdotH * roughness;
|
||||
float Fd = lerp(1.0, Fd90, FL) * lerp(1.0, Fd90, FV);
|
||||
|
||||
// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
|
||||
// 1.25 scale is used to (roughly) preserve albedo
|
||||
// Fss90 used to "flatten" retroreflection based on roughness
|
||||
float Fss90 = LdotH * LdotH * roughness;
|
||||
float Fss = lerp(1.0, Fss90, FL) * lerp(1.0, Fss90, FV);
|
||||
float ss = 1.25 * (Fss * (1 / (NdotL + NdotV) - .5) + .5);
|
||||
|
||||
// specular
|
||||
float aspect = sqrt(1 - anisotropic * .9);
|
||||
float ax = max(.001, sqr(roughness) / aspect);
|
||||
float ay = max(.001, sqr(roughness) * aspect);
|
||||
float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
|
||||
float FH = SchlickFresnel(LdotH);
|
||||
float3 Fs = lerp(Cspec0, float3(1), FH);
|
||||
float Gs;
|
||||
Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay);
|
||||
Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay);
|
||||
|
||||
// sheen
|
||||
float3 Fsheen = FH * sheen * Csheen;
|
||||
|
||||
// clearcoat (ior = 1.5 -> F0 = 0.04)
|
||||
float Dr = GTR1(NdotH, lerp(.1, .001, clearCoatGloss));
|
||||
float Fr = lerp(.04, 1.0, FH);
|
||||
float Gr = smithG_GGX(NdotL, .25) * smithG_GGX(NdotV, .25);
|
||||
|
||||
return ((1 / PI) * lerp(Fd, ss, subsurface) * Cdlin + Fsheen)
|
||||
* (1 - metallic)
|
||||
+ Gs * Fs * Ds + .25 * clearCoat * Gr * Fr * Dr;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
const static float PI = 3.1415926535897932f;
|
||||
const static uint MAX_PARTICLES = 65536;
|
||||
const static uint BLOCK_SIZE = 8;
|
||||
|
||||
cbuffer ScreenToViewParams
|
||||
{
|
||||
float4x4 inverseProjection;
|
||||
float2 screenDimensions;
|
||||
}
|
||||
|
||||
// Convert clip space coordinates to view space
|
||||
float4 clipToView( float4 clip )
|
||||
{
|
||||
// View space position.
|
||||
float4 view = mul( inverseProjection, clip );
|
||||
// Perspective projection.
|
||||
view = view / view.w;
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
// Convert screen space coordinates to view space.
|
||||
float4 screenToView( float4 screen )
|
||||
{
|
||||
// Convert to normalized texture coordinates
|
||||
float2 texCoord = screen.xy / screenDimensions;
|
||||
|
||||
// Convert to clip space
|
||||
float4 clip = float4( float2( texCoord.x, -texCoord.y ) * 2.0f - 1.0f, screen.z, screen.w );
|
||||
|
||||
return clipToView( clip );
|
||||
}
|
||||
|
||||
struct Plane
|
||||
{
|
||||
float3 n;
|
||||
float d;
|
||||
float3 p0;
|
||||
float3 p1;
|
||||
float3 p2;
|
||||
};
|
||||
|
||||
struct Frustum
|
||||
{
|
||||
Plane planes[4];
|
||||
};
|
||||
Plane computePlane(float3 p0, float3 p1, float3 p2)
|
||||
{
|
||||
Plane plane;
|
||||
|
||||
float3 v0 = p2 - p0;
|
||||
float3 v2 = p1 - p0;
|
||||
|
||||
plane.n = normalize(cross(v0, v2));
|
||||
|
||||
plane.d = dot(plane.n, p0);
|
||||
plane.p0 = p0;
|
||||
plane.p1 = p1;
|
||||
plane.p2 = p2;
|
||||
|
||||
return plane;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import LightEnv;
|
||||
import Material;
|
||||
import BRDF;
|
||||
import InputGeometry;
|
||||
|
||||
struct FlatColorMaterial : IMaterial
|
||||
{
|
||||
float3 diffuseColor;
|
||||
float specularity;
|
||||
|
||||
typedef BlinnPhong BRDF;
|
||||
BlinnPhong prepare(MaterialPixelParameter input)
|
||||
{
|
||||
BlinnPhong result;
|
||||
result.baseColor = diffuseColor;
|
||||
result.specular = specularity;
|
||||
result.normal = normalize(input.normal);
|
||||
result.roughness = 0.5;
|
||||
result.specularTint = 0;
|
||||
result.anisotropic = 1;
|
||||
result.sheen = 1;
|
||||
result.sheenTint = 0.5;
|
||||
result.clearCoat = 0;
|
||||
result.clearCoatGloss = 0;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
interface IVertexShaderInput
|
||||
{
|
||||
//internally, the vertex layout is stored in vec4 for faster access,
|
||||
//but here we unwrap them for convenience
|
||||
float3 getVertexPosition();
|
||||
float2 getTexCoords();
|
||||
float3 getNormal();
|
||||
float3 getTangent();
|
||||
float3 getBiTangent();
|
||||
};
|
||||
|
||||
struct PositionOnlyVertexInput : IVertexShaderInput
|
||||
{
|
||||
float4 position;
|
||||
|
||||
float3 getVertexPosition() { return position.xyz; }
|
||||
float2 getTexCoords() { return float2(0, 0); }
|
||||
float3 getNormal() { return float3(0, 1, 0); }
|
||||
float3 getTangent() { return float3(1, 0, 0); }
|
||||
float3 getBiTangent() { return float3(0, 0, 1); }
|
||||
};
|
||||
|
||||
struct PositionAndNormalVertexInput : IVertexShaderInput
|
||||
{
|
||||
float4 position;
|
||||
float4 normal;
|
||||
|
||||
float3 getVertexPosition() { return position.xyz; }
|
||||
float2 getTexCoords() { return float2(0, 0); }
|
||||
float3 getNormal() { return normal.xyz; }
|
||||
float3 getTangent() { return float3(1, 0, 0); }
|
||||
float3 getBiTangent() { return float3(0, 0, 1); }
|
||||
};
|
||||
|
||||
struct InputGeometry : IVertexShaderInput
|
||||
{
|
||||
float4 position;
|
||||
float2 texCoord;
|
||||
float4 normal;
|
||||
float4 tangent;
|
||||
float4 bitangent;
|
||||
|
||||
float3 getVertexPosition() { return position.xyz; }
|
||||
float2 getTexCoords() { return texCoord; }
|
||||
float3 getNormal() { return normal.xyz; }
|
||||
float3 getTangent() { return tangent.xyz; }
|
||||
float3 getBiTangent() { return bitangent.xyz; }
|
||||
};
|
||||
|
||||
struct MaterialPixelParameter
|
||||
{
|
||||
float3 position;
|
||||
float2 texCoord;
|
||||
float3 viewDir;
|
||||
float3 normal;
|
||||
float3 tangent;
|
||||
float3 biTangent;
|
||||
float4 clipPosition;
|
||||
float3 transformLocalToWorld(float3 input)
|
||||
{
|
||||
float3 unitNormal = normalize(normal);
|
||||
float3 unitTangent = normalize(tangent);
|
||||
unitTangent = normalize(unitTangent - dot(unitTangent, unitNormal) * unitNormal);
|
||||
float3 unitBitangent = cross(unitTangent, unitNormal);
|
||||
float3x3 tbn = float3x3(unitTangent, unitBitangent, unitNormal);
|
||||
float3 result = mul(tbn, input);
|
||||
result = normalize(result);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,70 @@
|
||||
import InputGeometry;
|
||||
import BRDF;
|
||||
import Common;
|
||||
|
||||
interface ILightEnv
|
||||
{
|
||||
float3 illuminate<B:IBRDF>(InputGeometry input, B brdf, float3 wo);
|
||||
};
|
||||
|
||||
struct DirectionalLight : ILightEnv
|
||||
{
|
||||
float4 color;
|
||||
float4 direction;
|
||||
float4 intensity;
|
||||
|
||||
float3 illuminate<B:IBRDF>(MaterialPixelParameter input, B brdf, float3 wo)
|
||||
{
|
||||
return intensity.xyz * brdf.evaluate(wo, direction.xyz, input.normal, input.tangent, input.biTangent, color.xyz);
|
||||
}
|
||||
};
|
||||
|
||||
struct PointLight : ILightEnv
|
||||
{
|
||||
float4 positionWS;
|
||||
float4 positionVS;
|
||||
float3 color;
|
||||
float range;
|
||||
|
||||
float3 illuminate<B:IBRDF>(MaterialPixelParameter input, B brdf, float3 viewDir)
|
||||
{
|
||||
float3 lightVec = positionWS.xyz - input.position;
|
||||
float d = length(lightVec);
|
||||
float3 direction = normalize(lightVec);
|
||||
float illuminance = max(1 - d / range, 0);
|
||||
return illuminance * brdf.evaluate(viewDir, direction, input.normal, input.tangent, input.biTangent, color);
|
||||
}
|
||||
|
||||
bool insidePlane(Plane plane)
|
||||
{
|
||||
return dot(plane.n, positionVS.xyz) - plane.d < -range;
|
||||
}
|
||||
|
||||
bool insideFrustum(Frustum frustum, float zNear, float zFar)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
//if(positionVS.z - range > zNear || positionVS.z + range < zFar)
|
||||
{
|
||||
// result = false;
|
||||
}
|
||||
for(int i = 0; i < 4 && result; ++i)
|
||||
{
|
||||
if(insidePlane(frustum.planes[i]))
|
||||
{
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
#define MAX_DIRECTIONAL_LIGHTS 4
|
||||
#define MAX_POINT_LIGHTS 256
|
||||
struct Lights
|
||||
{
|
||||
DirectionalLight directionalLights[MAX_DIRECTIONAL_LIGHTS];
|
||||
PointLight pointLights[MAX_POINT_LIGHTS];
|
||||
uint numDirectionalLights;
|
||||
uint numPointLights;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import Common;
|
||||
import BRDF;
|
||||
import InputGeometry;
|
||||
|
||||
interface IMaterial
|
||||
{
|
||||
associatedtype BRDF : IBRDF;
|
||||
BRDF prepare(MaterialPixelParameter geometry);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import Material;
|
||||
import InputGeometry;
|
||||
|
||||
struct ParallaxMaterial : IMaterial
|
||||
{
|
||||
Texture2D<float4> diffuseTexture;
|
||||
Texture2D<float4> specularTexture;
|
||||
Texture2D<float4> displacementTexture;
|
||||
SamplerState textureSampler;
|
||||
float specularity;
|
||||
|
||||
typedef BlinnPhong BRDF;
|
||||
BlinnPhong prepare(InputGeometry geometry)
|
||||
{
|
||||
BlinnPhong blinn;
|
||||
blinn.baseColor = diffuseTexture.Sample(textureSampler, geometry.getTexCoords()).xyz;
|
||||
blinn.specularColor = specularTexture.Sample(textureSampler, geometry.getTexCoords()).xyz;
|
||||
blinn.specular = specularity;
|
||||
return blinn;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
struct Particle
|
||||
{
|
||||
float3 position;
|
||||
float mass;
|
||||
float3 velocity;
|
||||
float age;
|
||||
float3 forceAccumulator;
|
||||
float life;
|
||||
float color;
|
||||
float3 pad;
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
import LightEnv;
|
||||
import Material;
|
||||
import BRDF;
|
||||
import InputGeometry;
|
||||
|
||||
struct TexturedMaterial : IMaterial
|
||||
{
|
||||
Texture2D diffuseTexture;
|
||||
Texture2D specularTexture;
|
||||
Texture2D normalTexture;
|
||||
float uvScale;
|
||||
float metallic = 0;
|
||||
float subsurface = 0;
|
||||
float roughness = 0.5;
|
||||
float specularTint = 0;
|
||||
float anisotropic = 0;
|
||||
float sheen = 0;
|
||||
float sheenTint = 0.5f;
|
||||
float clearCoat = 0;
|
||||
float clearCoatGloss = 1;
|
||||
SamplerState textureSampler;
|
||||
|
||||
typedef BlinnPhong BRDF;
|
||||
BlinnPhong prepare(MaterialPixelParameter geometry)
|
||||
{
|
||||
BlinnPhong result;
|
||||
result.baseColor = diffuseTexture.Sample(textureSampler, geometry.texCoord * uvScale).xyz;
|
||||
result.metallic = 0;
|
||||
float3 bumpMapNormal = normalTexture.Sample(textureSampler, geometry.texCoord * uvScale).xyz;
|
||||
bumpMapNormal = 2.0 * bumpMapNormal - float3(1.0, 1.0, 1.0);
|
||||
result.normal = geometry.transformLocalToWorld(bumpMapNormal);
|
||||
result.specular = specularTexture.Sample(textureSampler, geometry.texCoord * uvScale).x;
|
||||
result.roughness = roughness;
|
||||
result.specularTint = specularTint;
|
||||
result.anisotropic = anisotropic;
|
||||
result.sheen = sheen;
|
||||
result.sheenTint = sheenTint;
|
||||
result.clearCoat = clearCoat;
|
||||
result.clearCoatGloss = clearCoatGloss;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,485 @@
|
||||
// shaders.slang
|
||||
|
||||
//
|
||||
// This example builds on the simplistic shaders presented in the
|
||||
// "Hello, World" example by adding support for (intentionally
|
||||
// simplistic) surface materil and light shading.
|
||||
//
|
||||
// The code here is not meant to exemplify state-of-the-art material
|
||||
// and lighting techniques, but rather to show how a shader
|
||||
// library can be developed in a modular fashion without reliance
|
||||
// on the C preprocessor manual parameter-binding decorations.
|
||||
//
|
||||
|
||||
// We are going to define a simple model for surface material shading.
|
||||
//
|
||||
// The first building block in our model will be the representation of
|
||||
// the geometry attributes of a surface as fed into the material.
|
||||
//
|
||||
struct SurfaceGeometry
|
||||
{
|
||||
float3 position;
|
||||
float3 normal;
|
||||
|
||||
// TODO: tangent vectors would be the natural next thing to add here,
|
||||
// and would be required for anisotropic materials. However, the
|
||||
// simplistic model loading code we are currently using doesn't
|
||||
// produce tangents...
|
||||
//
|
||||
// float3 tangentU;
|
||||
// float3 tangentV;
|
||||
|
||||
// We store a single UV parameterization in these geometry attributes.
|
||||
// A more complex renderer might need support for multiple UV sets,
|
||||
// and indeed it might choose to use interfaces and generics to capture
|
||||
// the different requirements that different materials impose on
|
||||
// the available surface attributes. We won't go to that kind of
|
||||
// trouble for such a simple example.
|
||||
//
|
||||
float2 uv;
|
||||
};
|
||||
//
|
||||
// Next, we want to define the fundamental concept of a refletance
|
||||
// function, so that we can use it as a building block for other
|
||||
// parts of the system. This is a case where we are trying to
|
||||
// show how a proper physically-based renderer (PBR) might
|
||||
// decompose the problem using Slang, even though our simple
|
||||
// example is *not* physically based.
|
||||
//
|
||||
interface IBRDF
|
||||
{
|
||||
// Technically, a BRDF is only a function of the incident
|
||||
// (`wi`) and exitant (`wo`) directions, but for simplicity
|
||||
// we are passing in the surface normal (`N`) as well.
|
||||
//
|
||||
float3 evaluate(float3 wo, float3 wi, float3 N);
|
||||
};
|
||||
//
|
||||
// We can now define various implemntations of the `IBRDF` interface
|
||||
// that represent different reflectance functions we want to support.
|
||||
// For now we keep things simple by defining about the simplest
|
||||
// reflectance function we can think of: the Blinn-Phong reflectance
|
||||
// model:
|
||||
//
|
||||
struct BlinnPhong : IBRDF
|
||||
{
|
||||
// Blinn-Phong needs diffuse and specular reflectances, plus
|
||||
// a specular exponent value (which relates to "roughness"
|
||||
// in more modern physically-based models).
|
||||
//
|
||||
float3 kd;
|
||||
float3 ks;
|
||||
float specularity;
|
||||
|
||||
// Here we implement the one requirement of the `IBRDF` interface
|
||||
// for our concrete implementation, using a textbook definition
|
||||
// of Blinng-Phong shading.
|
||||
//
|
||||
// Note: our "BRDF" definition here folds the N-dot-L term into
|
||||
// the evlauation of the reflectance function in case there are
|
||||
// useful algebraic simplifications this enables.
|
||||
//
|
||||
float3 evaluate(float3 V, float3 L, float3 N)
|
||||
{
|
||||
float nDotL = saturate(dot(N, L));
|
||||
float3 H = normalize(L + V);
|
||||
float nDotH = saturate(dot(N, H));
|
||||
|
||||
return kd*nDotL + ks*pow(nDotH, specularity);
|
||||
}
|
||||
};
|
||||
//
|
||||
// It is important to note that a reflectance function is *not*
|
||||
// a "material." In most cases, a material will have spatially-varying
|
||||
// properties so that it cannot be summarized as a single `IBRDF`
|
||||
// instance.
|
||||
//
|
||||
// Thus a "material" is a value that can produce a BRDF for any point
|
||||
// on a surface (e.g., by sampling texture maps, etc.).
|
||||
//
|
||||
interface IMaterial
|
||||
{
|
||||
// Different concrete material implementations might yield BRDF
|
||||
// values with different types. E.g., one material might yield
|
||||
// reflectance functions using `BlinnPhong` while another uses
|
||||
// a much more complicated/accurate representation.
|
||||
//
|
||||
// We encapsulate the choice of BRDF parameters/evaluation in
|
||||
// our material interface with an "associated type." In the
|
||||
// simplest terms, think of this as an interface requirement
|
||||
// that is a type, instead of a method.
|
||||
//
|
||||
// (If you are C++-minded, you might think of this as akin to
|
||||
// how every container provided an `iterator` type, but different
|
||||
// containers may have different types of iterators)
|
||||
//
|
||||
associatedtype BRDF : IBRDF;
|
||||
|
||||
// For our simple example program, it is enough for a material to
|
||||
// be able to return a BRDF given a point on the surface.
|
||||
//
|
||||
// A more complex implementation of material shading might also
|
||||
// have the material return updated surface geometry to reflect
|
||||
// the result of normal mapping, occlusion mapping, etc. or
|
||||
// return an opacity/coverage value for partially transparent
|
||||
// surfaces.
|
||||
//
|
||||
BRDF prepare(SurfaceGeometry geometry);
|
||||
};
|
||||
|
||||
// We will now define a trivial first implementation of the material
|
||||
// interface, which uses our Blinn-Phong BRDF with uniform values
|
||||
// for its parameters.
|
||||
//
|
||||
// Note that this implemetnation is being provided *after* the
|
||||
// shader parameter `gMaterial` is declared, so that there is no
|
||||
// assumption in the shader code that `gMaterial` will be plugged
|
||||
// in using an instance of `SimpleMaterial`
|
||||
//
|
||||
//
|
||||
struct SimpleMaterial : IMaterial
|
||||
{
|
||||
// We declare the properties we need as fields of the material type.
|
||||
// When `SimpleMaterial` is used for `TMaterial` above, then
|
||||
// `gMaterial` will be a `ParameterBlock<SimpleMaterial>`, and these
|
||||
// parameters will be allocated to a constant buffer that is part of
|
||||
// that parameter block.
|
||||
//
|
||||
// TODO: A future version of this example will include texture parameters
|
||||
// here to show that they are declared just like simple uniforms.
|
||||
//
|
||||
float3 diffuseColor;
|
||||
float3 specularColor;
|
||||
float specularity;
|
||||
|
||||
// To satisfy the requirements of the `IMaterial` interface, our
|
||||
// material type needs to provide a suitable `BRDF` type. We
|
||||
// do this by using a simple `typedef`, although a nested
|
||||
// `struct` type can also satisfy an associated type requirement.
|
||||
//
|
||||
// A future version of the Slang compiler may allow the "right"
|
||||
// associated type definition to be inferred from the signature
|
||||
// of the `prepare()` method below.
|
||||
//
|
||||
typedef BlinnPhong BRDF;
|
||||
|
||||
BlinnPhong prepare(SurfaceGeometry geometry)
|
||||
{
|
||||
BlinnPhong brdf;
|
||||
brdf.kd = diffuseColor;
|
||||
brdf.ks = specularColor;
|
||||
brdf.specularity = specularity;
|
||||
return brdf;
|
||||
}
|
||||
};
|
||||
//
|
||||
// Note that no other code in this file statically
|
||||
// references the `SimpleMaterial` type, and instead
|
||||
// it is up to the application to "plug in" this type,
|
||||
// or another `IMaterial` implementation for the
|
||||
// `TMaterial` parameter.
|
||||
//
|
||||
|
||||
// A light, or an entire lighting *environment* is an object
|
||||
// that can illuminate a surface using some BRDF implemented
|
||||
// with our abstractions above.
|
||||
//
|
||||
interface ILightEnv
|
||||
{
|
||||
// The `illuminate` method is intended to integrate incoming
|
||||
// illumination from this light (environment) incident at the
|
||||
// surface point given by `g` (which has the reflectance function
|
||||
// `brdf`) and reflected into the outgoing direction `wo`.
|
||||
//
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo);
|
||||
//
|
||||
// Note that the `illuminate()` method is allowed as an interface
|
||||
// requirement in Slang even though it is a generic. Constract that
|
||||
// with C++ where a `template` method cannot be `virtual`.
|
||||
};
|
||||
|
||||
// Given the `ILightEnv` interface, we can write up almost textbook
|
||||
// definition of directional and point lights.
|
||||
|
||||
struct DirectionalLight : ILightEnv
|
||||
{
|
||||
float3 direction;
|
||||
float3 intensity;
|
||||
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo)
|
||||
{
|
||||
return intensity * brdf.evaluate(wo, direction, g.normal);
|
||||
}
|
||||
};
|
||||
struct PointLight : ILightEnv
|
||||
{
|
||||
float3 position;
|
||||
float3 intensity;
|
||||
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo)
|
||||
{
|
||||
float3 delta = position - g.position;
|
||||
float d = length(delta);
|
||||
float3 direction = normalize(delta);
|
||||
float3 illuminance = intensity / (d*d);
|
||||
return illuminance * brdf.evaluate(wo, direction, g.normal);
|
||||
}
|
||||
};
|
||||
|
||||
// In most cases, a shader entry point will only be specialized for a single
|
||||
// material, but interesting rendering almost always needs multiple lights.
|
||||
// For that reason we will next define types to represent *composite* lighting
|
||||
// environment with multiple lights.
|
||||
//
|
||||
// A naive approach might be to have a single undifferntiated list of lights
|
||||
// where any type of light may appear at any index, but this would lose all
|
||||
// of the benefits of static specialization: we would have to perform dynamic
|
||||
// branching to determine what kind of light is stored at each index.
|
||||
//
|
||||
// Instead, we will start with a type for *homogeneous* arrays of lights:
|
||||
//
|
||||
struct LightArray<L : ILightEnv, let N : int> : ILightEnv
|
||||
{
|
||||
// The `LightArray` type has two generic parameters:
|
||||
//
|
||||
// - `L` is a type parameter, representing the type of lights that will be in our array
|
||||
// - `N` is a generic *value* parameter, representing the maximum number of lights allowed
|
||||
//
|
||||
// Slang's support for generic value parameters is currently experimental,
|
||||
// and the syntax might change.
|
||||
|
||||
int count;
|
||||
L lights[N];
|
||||
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo)
|
||||
{
|
||||
// Our light array integrates illumination by naively summing
|
||||
// contributions from all the lights in the array (up to `count`).
|
||||
//
|
||||
// A more advanced renderer might try apply sampling techniques
|
||||
// to pick a subset of lights to sample.
|
||||
//
|
||||
float3 sum = 0;
|
||||
for( int ii = 0; ii < count; ++ii )
|
||||
{
|
||||
sum += lights[ii].illuminate(g, brdf, wo);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
};
|
||||
|
||||
// `LightArray` can handle multiple lights as long as they have the
|
||||
// same type, but we need a way to have a scene with multiple lights
|
||||
// of different types *without* losing static specialization.
|
||||
//
|
||||
// The `LightPair<T,U>` type supports this in about the simplest way
|
||||
// possible, by aggregating a light (environment) of type `T` and
|
||||
// one of type `U`. Those light environments might themselves be
|
||||
// `LightArray`s or `LightPair`s, so that arbitrarily complex
|
||||
// environments can be created from just these two composite types.
|
||||
//
|
||||
// This is probably a good place to insert a reminder the Slang's
|
||||
// generics are *not* C++ templates, so that the error messages
|
||||
// produced when working with these types are in general reasonable,
|
||||
// and this is *not* any form of "template metaprogramming."
|
||||
//
|
||||
// That said, we expect that future versions of Slang will make
|
||||
// defining composite types light this a bit less cumbersome.
|
||||
//
|
||||
struct LightPair<T : ILightEnv, U : ILightEnv> : ILightEnv
|
||||
{
|
||||
T first;
|
||||
U second;
|
||||
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo)
|
||||
{
|
||||
return first.illuminate(g, brdf, wo)
|
||||
+ second.illuminate(g, brdf, wo);
|
||||
}
|
||||
};
|
||||
|
||||
// As a final (degenerate) case, we will define a light
|
||||
// environment with *no* lights, which contributes no illumination.
|
||||
//
|
||||
struct EmptyLightEnv : ILightEnv
|
||||
{
|
||||
float3 illuminate<B:IBRDF>(SurfaceGeometry g, B brdf, float3 wo)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// The code above constitutes the "shader library" for our
|
||||
// application, while the code below this point is the
|
||||
// implementation of a simple forward rendering pass
|
||||
// using that library.
|
||||
//
|
||||
// While the shader library has used many of Slang's advanced
|
||||
// mechanisms, the vertex and fragment shaders will be
|
||||
// much more modest, and hopefully easier to follow.
|
||||
|
||||
|
||||
// We will start with a `struct` for per-view parameters that
|
||||
// will be allocated into a `ParameterBlock`.
|
||||
//
|
||||
// As written, this isn't very different from using an HLSL
|
||||
// `cbuffer` declaration, but importantly this code will
|
||||
// continue to work if we add one or more resources (e.g.,
|
||||
// an enironment map texture) to the `PerView` type.
|
||||
//
|
||||
struct PerView
|
||||
{
|
||||
float4x4 viewProjection;
|
||||
float3 eyePosition;
|
||||
};
|
||||
ParameterBlock<PerView> gViewParams;
|
||||
|
||||
// Declaring a block for per-model parameter data is
|
||||
// similarly simple.
|
||||
//
|
||||
struct PerModel
|
||||
{
|
||||
float4x4 modelTransform;
|
||||
float4x4 inverseTransposeModelTransform;
|
||||
};
|
||||
ParameterBlock<PerModel> gModelParams;
|
||||
|
||||
// We want our shader to work with any kind of lighting environment
|
||||
// - that is, and type that implements `ILightEnv`. Furthermore,
|
||||
// we want the parameters of that lighting environment to be passed
|
||||
// as parameter block - `ParameterBlock<L>` for some type `L`.
|
||||
//
|
||||
// We handle this by defining a global generic type parameter for
|
||||
// our shader, and constrainting it to implement `ILightEnv`...
|
||||
//
|
||||
type_param TLightEnv : ILightEnv;
|
||||
//
|
||||
// ... and then defining a parameter block that uses that type
|
||||
// parameter as the "element type" of the block:
|
||||
//
|
||||
ParameterBlock<TLightEnv> gLightEnv;
|
||||
|
||||
// Our handling of the material parameter for our shader
|
||||
// is quite similar to the case for the lighting environment:
|
||||
//
|
||||
type_param TMaterial : IMaterial;
|
||||
ParameterBlock<TMaterial> gMaterial;
|
||||
|
||||
// Our vertex shader entry point is only marginally more
|
||||
// complicated than the Hello World example. We will
|
||||
// start by declaring the various "connector" `struct`s.
|
||||
//
|
||||
struct AssembledVertex
|
||||
{
|
||||
float3 position : POSITION;
|
||||
float3 normal : NORMAL;
|
||||
float2 uv : UV;
|
||||
};
|
||||
struct CoarseVertex
|
||||
{
|
||||
float3 worldPosition;
|
||||
float3 worldNormal;
|
||||
float2 uv;
|
||||
};
|
||||
struct VertexStageOutput
|
||||
{
|
||||
CoarseVertex coarseVertex : CoarseVertex;
|
||||
float4 sv_position : SV_Position;
|
||||
};
|
||||
|
||||
// Perhaps most interesting new feature of the entry
|
||||
// point decalrations is that we use a `[shader(...)]`
|
||||
// attribute (as introduced in HLSL Shader Model 6.x)
|
||||
// in order to tag our entry points.
|
||||
//
|
||||
// This attribute informs the Slang compiler which
|
||||
// functions are intended to be compiled as shader
|
||||
// entry points (and what stage they target), so that
|
||||
// the programmer no longer needs to specify the
|
||||
// entry point name/stage through the API (or on
|
||||
// the command line when using `slangc`).
|
||||
//
|
||||
// While HLSL added this feature only in newer versions,
|
||||
// the Slang compiler supports this attribute across
|
||||
// *all* targets, so that it is okay to use whether you
|
||||
// want DXBC, DXIL, or SPIR-V output.
|
||||
//
|
||||
[shader("vertex")]
|
||||
VertexStageOutput vertexMain(
|
||||
AssembledVertex assembledVertex)
|
||||
{
|
||||
VertexStageOutput output;
|
||||
|
||||
float3 position = assembledVertex.position;
|
||||
float3 normal = assembledVertex.normal;
|
||||
float2 uv = assembledVertex.uv;
|
||||
|
||||
float3 worldPosition = mul(gModelParams.modelTransform, float4(position, 1.0)).xyz;
|
||||
float3 worldNormal = mul(gModelParams.inverseTransposeModelTransform, float4(normal, 0.0)).xyz;
|
||||
|
||||
output.coarseVertex.worldPosition = worldPosition;
|
||||
output.coarseVertex.worldNormal = worldNormal;
|
||||
output.coarseVertex.uv = uv;
|
||||
|
||||
output.sv_position = mul(gViewParams.viewProjection, float4(worldPosition, 1.0));
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
// Our fragment shader is almost trivial, with the most interesting
|
||||
// thing being how it uses the `TMaterial` type parameter (through the
|
||||
// value stored in the `gMaterial` parameter block) to dispatch to
|
||||
// the correct implementation of the `getDiffuseColor()` method
|
||||
// in the `IMaterial` interface.
|
||||
//
|
||||
// The `gMaterial` parameter block declaration thus serves not only
|
||||
// to group certain shader parameters for efficient CPU-to-GPU
|
||||
// communication, but also to select the code that will execute
|
||||
// in specialized versions of the `fragmentMain` entry point.
|
||||
//
|
||||
[shader("fragment")]
|
||||
float4 fragmentMain(
|
||||
CoarseVertex coarseVertex : CoarseVertex) : SV_Target
|
||||
{
|
||||
// We start by using our interpolated vertex attributes
|
||||
// to construct the local surface geometry that we will
|
||||
// use for material evaluation.
|
||||
//
|
||||
SurfaceGeometry g;
|
||||
g.position = coarseVertex.worldPosition;
|
||||
g.normal = normalize(coarseVertex.worldNormal);
|
||||
g.uv = coarseVertex.uv;
|
||||
|
||||
float3 V = normalize(gViewParams.eyePosition - g.position);
|
||||
|
||||
// Next we prepare the material, which involves running
|
||||
// any "pattern generation" logic of the material (e.g.,
|
||||
// sampling and blending texture layers), to produce
|
||||
// a BRDF suitable for evaluating under illumination
|
||||
// from different light sources.
|
||||
//
|
||||
// Note that the return type here is `TMaterial.BRDF`,
|
||||
// which is the `BRDF` type *associated* with the (unknown)
|
||||
// `TMaterial` type. When `TMaterial` gets substituted for
|
||||
// a concrete type later (e.g., `SimpleMaterial`) this
|
||||
// will resolve to a concrete type too (e.g., `SimpleMaterial.BRDF`
|
||||
// which is an alias for `BlinnPhong`).
|
||||
//
|
||||
TMaterial.BRDF brdf = gMaterial.prepare(g);
|
||||
|
||||
// Now that we've done the first step of material evaluation
|
||||
// and sampled texture maps, etc., it is time to start
|
||||
// integrating incident light at our surface point.
|
||||
//
|
||||
// Because we've wrapped up the lighting environment as
|
||||
// a single (composite) object, this is as simple as calling
|
||||
// its `illuminate()` method. Our particular fragment shader
|
||||
// is thus abstracted from how the renderer chooses to structure
|
||||
// this integration step, somewhat similar to how an
|
||||
// `illuminance` loop in RenderMan Shading Language works.
|
||||
//
|
||||
|
||||
float3 color = gLightEnv.illuminate(g, brdf, V);
|
||||
|
||||
return float4(color, 1);
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
Reference in New Issue
Block a user