Redo of render paths

This commit is contained in:
Dynamitos
2020-06-02 11:46:18 +02:00
parent bb5b48698a
commit 356e6058fe
121 changed files with 3890 additions and 572 deletions
+59
View File
@@ -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;
}
}
+33
View File
@@ -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;
}
+39
View File
@@ -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);
}
+80
View File
@@ -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);
}
+92
View File
@@ -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);
}
+149
View File
@@ -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];
}
}
+75
View File
@@ -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;"
]
}
+152
View File
@@ -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;
}
};
+62
View File
@@ -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;
}
+27
View File
@@ -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;
}
};
+70
View File
@@ -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;
}
};
+70
View File
@@ -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;
};
+9
View File
@@ -0,0 +1,9 @@
import Common;
import BRDF;
import InputGeometry;
interface IMaterial
{
associatedtype BRDF : IBRDF;
BRDF prepare(MaterialPixelParameter geometry);
};
+21
View File
@@ -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;
}
};
+12
View File
@@ -0,0 +1,12 @@
struct Particle
{
float3 position;
float mass;
float3 velocity;
float age;
float3 forceAccumulator;
float life;
float color;
float3 pad;
};
+42
View File
@@ -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;
}
};
+485
View File
@@ -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