Skip to content

Commit 6e50eba

Browse files
committed
Engine: More PBR shader support and full PBR textures support added (baseColor, normalTexture, metallicRoughnessTexture, emissiveTexture and occlusionTexture)
1 parent d3cbef8 commit 6e50eba

26 files changed

+501
-117
lines changed

Alimer.slnx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
</Folder>
2222
<Folder Name="/assets/shaders/">
2323
<File Path="assets/Shaders/Alimer.hlsli" />
24+
<File Path="assets/Shaders/BRDF.hlsli" />
2425
<File Path="assets/Shaders/IBL.slang" />
2526
<File Path="assets/Shaders/Math.hlsli" />
2627
<File Path="assets/Shaders/PBR.slang" />

assets/Shaders/BRDF.hlsli

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright (c) Amer Koleci and Contributors.
2+
// Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
3+
4+
#ifndef _ALIMER_BRDF__
5+
#define _ALIMER_BRDF__
6+
7+
#include "Math.hlsli"
8+
9+
float3 FresnelSchlick(in float LdotH, in float3 F0)
10+
{
11+
return F0 + (1.f - F0) * pow(1.f - LdotH, 5.f);
12+
}
13+
14+
float3 FresnelSchlickRoughness(float LdotH, float3 F0, float roughness)
15+
{
16+
float v = 1.0f - roughness;
17+
return F0 + (max(float3(v, v, v), F0) - F0) * pow(1.f - LdotH, 5.f);
18+
}
19+
20+
float DistributionGGX(in float3 N, in float3 H, in float roughness)
21+
{
22+
float a = roughness * roughness;
23+
float a2 = a * a;
24+
float NdotH = saturate(dot(N, H));
25+
float NdotH2 = NdotH * NdotH;
26+
27+
float denom = (NdotH2 * (a2 - 1.f) + 1.f);
28+
return a2 / max(denom * denom * Pi, 0.001f);
29+
}
30+
31+
float GeometrySchlickGGX(in float NdotV, in float roughness)
32+
{
33+
const float r = roughness + 1;
34+
const float k = (r * r) / 8;
35+
36+
const float num = NdotV;
37+
const float denom = NdotV * (1 - k) + k;
38+
39+
return num / denom;
40+
}
41+
42+
float GeometrySmith(in float3 N, in float3 V, in float3 L, in float roughness)
43+
{
44+
const float NdotV = max(dot(N, V), 0);
45+
const float NdotL = max(dot(N, L), 0);
46+
const float ggx2 = GeometrySchlickGGX(NdotV, roughness);
47+
const float ggx1 = GeometrySchlickGGX(NdotL, roughness);
48+
return ggx1 * ggx2;
49+
}
50+
51+
// From https://www.unrealengine.com/en-US/blog/physically-based-shading-on-mobile
52+
float2 envBRDFApprox(in float roughness, in float NdotV)
53+
{
54+
float4 c0 = float4(-1.f, -0.0275f, -0.572f, 0.022f);
55+
float4 c1 = float4(1.f, 0.0425f, 1.04f, -0.04f);
56+
float4 r = roughness * c0 + c1;
57+
float a004 = min(r.x * r.x, exp2(-9.28f * NdotV)) * r.x + r.y;
58+
return float2(-1.04f, 1.04f) * a004 + r.zw;
59+
}
60+
61+
#endif // _ALIMER_BRDF__

assets/Shaders/PBR.slang

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Alimer.hlsli"
2-
#include "VertexInputOutput.hlsli"
2+
#include "BRDF.hlsli"
33
#include "ShaderTypes.h"
4+
#include "VertexInputOutput.hlsli"
45

56
struct SurfaceInfo
67
{
@@ -25,10 +26,51 @@ struct SurfaceInfo
2526
float3 F0;
2627
};
2728

29+
float3 getDiffuseLightColor(in float3 N)
30+
{
31+
// TODO
32+
// float width, height, numberOfLevels;
33+
// environmentTexture.GetDimensions(0, width, height, numberOfLevels);
34+
35+
// const float diffuseLevel = float(numberOfLevels - 1);
36+
// return environmentTexture.SampleLevel(environmentSampler, N, diffuseLevel).rgb;
37+
return float3(1.0f, 1.0f, 1.0f);
38+
}
39+
40+
float3 getSpecularLightColor(in float3 R, in float roughness)
41+
{
42+
// TODO
43+
// float width, height, numberOfLevels;
44+
// environmentTexture.GetDimensions(0, width, height, numberOfLevels);
45+
//
46+
// const float rough = numberOfLevels * roughness * (2.0f - roughness);
47+
// return environmentTexture.SampleLevel(environmentSampler, R, rough).rgb;
48+
return float3(1.0f, 1.0f, 1.0f);
49+
}
50+
51+
float3 pbrSurfaceColorIbl(in SurfaceInfo surface)
52+
{
53+
float3 kS = FresnelSchlickRoughness(surface.NdotV, surface.F0, surface.roughness);
54+
float3 kD = (float3(1.f, 1.f, 1.f) - kS) * (1.f - surface.metalness);
55+
float3 irradiance = getDiffuseLightColor(surface.N);
56+
float3 diffuse = irradiance * surface.diffuseColor;
57+
58+
float3 prefilteredColor = getSpecularLightColor(surface.R, surface.roughness);
59+
float2 envBrdf = envBRDFApprox(surface.roughness, surface.NdotV);
60+
float3 specular = prefilteredColor * (surface.specularColor * envBrdf.x + envBrdf.y);
61+
62+
float3 ambient = (kD * diffuse + specular) * surface.ao;
63+
return ambient;
64+
}
65+
2866
// Material (space 0)
29-
ConstantBuffer<PBRMaterialData> material : register(b0);
67+
ConstantBuffer<PBRMaterialUniforms> material : register(b0);
3068
Texture2D<float4> baseColorTexture : register(t0);
3169
Texture2D<float4> normalTexture : register(t1);
70+
Texture2D<float4> metallicRoughnessTexture : register(t2);
71+
Texture2D<float4> emissiveTexture : register(t3);
72+
Texture2D<float4> occlusionTexture : register(t4);
73+
// Texture2D<float4> transmissionTexture : register(t5, space1);
3274
SamplerState pbrSampler : register(s0);
3375

3476
[shader("pixel")]
@@ -52,13 +94,59 @@ float4 fragmentMain(in VertexOutput input, in bool isFrontFace: SV_IsFrontFace)
5294
{
5395
float3 N = float3(normalTexture.Sample(pbrSampler, input.TexCoord0).rg, 1.f);
5496
N = N * 2.f - 1.f;
55-
//N.rg *= material.GetNormalMapStrength();
97+
// N.rg *= material.GetNormalMapStrength();
5698
surface.N = normalize(mul(N, TBN));
5799
if (!isFrontFace) // MATERIAL_DOUBLE_SIDED?
58100
{
59101
surface.N = -surface.N;
60102
}
61103
}
62104

63-
return baseColor;
105+
surface.NdotV = saturate(dot(surface.N, surface.V));
106+
surface.R = reflect(-surface.V, surface.N);
107+
108+
const float3 color = input.Color * baseColor.rgb;
109+
surface.alpha = baseColor.a;
110+
surface.ao = material.occlusionStrength * occlusionTexture.Sample(pbrSampler, input.TexCoord0).r;
111+
surface.emissive = material.emissiveFactor * emissiveTexture.Sample(pbrSampler, input.TexCoord0).rgb;
112+
113+
const float4 metallicRoughnessMap = metallicRoughnessTexture.Sample(pbrSampler, input.TexCoord0);
114+
const float4 occlusionMap = occlusionTexture.Sample(pbrSampler, input.TexCoord0);
115+
const float4 emissiveMap = emissiveTexture.Sample(pbrSampler, input.TexCoord0);
116+
117+
surface.roughness = clamp(material.metallicRoughnessFactor.y * metallicRoughnessMap.g, 0.01f, 0.99f);
118+
surface.metalness = material.metallicRoughnessFactor.x * metallicRoughnessMap.b;
119+
120+
surface.diffuseColor = color * (1.0f - surface.metalness);
121+
surface.specularColor = color * surface.metalness;
122+
123+
// Constant normal incidence Fresnel factor for all dielectrics.
124+
static const float3 dielectricSpec = 0.04f;
125+
surface.F0 = lerp(dielectricSpec, color.rgb, surface.metalness);
126+
127+
// reflectance equation
128+
float3 Lo = pbrSurfaceColorIbl(surface);
129+
130+
#if defined(USE_SHADOWS)
131+
// Directional light
132+
[branch]
133+
if (lights.directionalLight.intensity > 0)
134+
{
135+
Lo += pbrDirectionalLight(lights.directionalLight, surface);
136+
}
137+
138+
// Point lights
139+
for (uint i = 0; i < lights.pointLightCount; i++)
140+
{
141+
PointLight light = pointLights[i];
142+
// calculate per-light radiance and add to outgoing radiance Lo
143+
Lo += pbrPointLight(light, surface);
144+
}
145+
146+
Lo += (surface.diffuseColor * surface.ao * lights.ambient) + surface.emissive;
147+
#else
148+
Lo += (surface.diffuseColor * surface.ao) + surface.emissive;
149+
#endif
150+
151+
return float4(Lo, surface.alpha);
64152
}

assets/Shaders/ShaderTypes.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ struct ALIGNMENT PerViewData
5151
};
5252

5353
/* TODO: pack and use half*/
54-
struct ALIGNMENT PBRMaterialData
54+
struct ALIGNMENT PBRMaterialUniforms
5555
{
5656
float4 baseColorFactor;
57+
float3 emissiveFactor;
58+
float _padding;
59+
float2 metallicRoughnessFactor;
60+
float occlusionStrength;
61+
float alphaCutoff;
5762
};
5863

5964
struct ALIGNMENT InstanceData

samples/Alimer.Samples/Engine/ScenePBRRendererSample.cs

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,26 +41,13 @@ public ScenePBRRendererSample(IServiceRegistry services)
4141
};
4242
MeshAsset meshAsset = meshImporter.Import(meshMetadata).Result;
4343

44-
Span<VertexPositionNormalTangentTexture> vertices = stackalloc VertexPositionNormalTangentTexture[meshAsset.Data!.VertexCount];
45-
for (int i = 0; i < meshAsset.Data.VertexCount; i++)
46-
{
47-
vertices[i] = new VertexPositionNormalTangentTexture(
48-
meshAsset.Data.Positions[i],
49-
meshAsset.Data.Normals[i],
50-
meshAsset.Data.Tangents[i],
51-
meshAsset.Data.Texcoords[i]
52-
);
53-
}
54-
55-
Mesh damagedHelmetMesh = new(vertices.Length, VertexPositionNormalTangentTexture.VertexAttributes, meshAsset.Data.Indices!.Length, IndexFormat.Uint32);
56-
damagedHelmetMesh.SetVertices(vertices);
57-
damagedHelmetMesh.SetIndices(meshAsset.Data.Indices!.AsSpan());
58-
damagedHelmetMesh.RecalculateBounds();
59-
damagedHelmetMesh.CreateGpuData(GraphicsDevice);
44+
Mesh damagedHelmetMesh = meshAsset.Mesh;
6045
_= ToDispose(damagedHelmetMesh);
6146

6247
{
63-
_damagedHelmetEntity = new("Damaged Helmet", new Vector3(0.0f, 2.0f, 0.0f));
48+
_damagedHelmetEntity = new("Damaged Helmet", meshAsset.Translation);
49+
_damagedHelmetEntity.Transform.Rotation = meshAsset.Rotation;
50+
_damagedHelmetEntity.Transform.Scale = meshAsset.Scale;
6451

6552
MeshComponent meshComponent = new(damagedHelmetMesh);
6653

samples/Alimer.Samples/Graphics/DrawMeshSample.cs

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ namespace Alimer.Samples.Graphics;
1414
public unsafe sealed class DrawMeshSample : GraphicsSampleBase
1515
{
1616
private readonly uint _indexCount;
17-
private readonly GraphicsBuffer _vertexBuffer;
18-
private readonly GraphicsBuffer _indexBuffer;
17+
private readonly Mesh _mesh;
1918
private readonly GraphicsBuffer _constantBuffer;
2019
private readonly Texture _texture;
2120
private readonly Sampler _sampler;
@@ -50,18 +49,7 @@ public DrawMeshSample(IServiceRegistry services, Window mainWindow)
5049
};
5150
MeshAsset meshAsset = meshImporter.Import(meshMetadata).Result;
5251

53-
Span<VertexPositionNormalTexture> vertices = stackalloc VertexPositionNormalTexture[meshAsset.Data!.VertexCount];
54-
for (int i = 0; i < meshAsset.Data.VertexCount; i++)
55-
{
56-
vertices[i] = new VertexPositionNormalTexture(meshAsset.Data.Positions[i], meshAsset.Data.Normals[i], meshAsset.Data.Texcoords[i]);
57-
}
58-
59-
//var data = MeshUtilities.CreateCube(5.0f);
60-
//_vertexBuffer = ToDispose(GraphicsDevice.CreateBuffer(data.Vertices, BufferUsage.Vertex));
61-
//_indexBuffer = ToDispose(GraphicsDevice.CreateBuffer(data.Indices, BufferUsage.Index));
62-
_vertexBuffer = ToDispose(GraphicsDevice.CreateBuffer(vertices, BufferUsage.Vertex));
63-
_indexBuffer = ToDispose(GraphicsDevice.CreateBuffer(meshAsset.Data.Indices!.AsSpan(), BufferUsage.Index));
64-
_indexCount = (uint)meshAsset.Data!.Indices.Length;
52+
_mesh = ToDispose(meshAsset.Mesh);
6553

6654
_constantBuffer = ToDispose(GraphicsDevice.CreateBuffer((ulong)sizeof(Matrix4x4), BufferUsage.Constant, MemoryType.Upload));
6755

@@ -131,9 +119,7 @@ public override void Draw(CommandBuffer context, Texture swapChainTexture)
131119
renderPassEncoder.SetBindGroup(1, _materialBindGroup);
132120
//context.SetPushConstants(0, worldViewProjection);
133121

134-
renderPassEncoder.SetVertexBuffer(0, _vertexBuffer);
135-
renderPassEncoder.SetIndexBuffer(_indexBuffer, IndexFormat.Uint16);
136-
renderPassEncoder.DrawIndexed(_indexCount);
122+
_mesh.Draw(renderPassEncoder, 1u);
137123
renderPassEncoder.EndEncoding();
138124
}
139125
}

samples/Alimer.Samples/SampleBrowserGame.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public static void Main()
7777
GraphicsBackend preferredGraphicsBackend = GraphicsBackend.Default;
7878

7979
#if !WINDOWS
80-
preferredGraphicsBackend = GraphicsBackend.Vulkan;
80+
//preferredGraphicsBackend = GraphicsBackend.Vulkan;
8181
//preferredGraphicsBackend = GraphicsBackend.Metal;
8282
#endif
8383

src/Alimer.SourceGenerators/AlimerTypes.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ namespace Alimer.SourceGenerators;
66
internal static class AlimerTypes
77
{
88
public const string Engine = "Alimer.Engine";
9+
public const string ExposeAttribute = $"Alimer.ExposeAttribute";
910
public const string DefaultEntitySystemAttribute = $"{Engine}.DefaultEntitySystemAttribute";
1011

1112
public const string AssetType = $"{Engine}.AssetType";

src/Alimer.SourceGenerators/Extensions.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ public static string AsString(this Accessibility accessibility)
1919
_ => "public"
2020
};
2121

22-
public static bool IsStruct(this SyntaxNode node)
23-
=> node is StructDeclarationSyntax;
22+
public static bool IsStruct(this SyntaxNode node) => node is StructDeclarationSyntax;
2423

25-
public static bool IsClass(this SyntaxNode node)
26-
=> node is ClassDeclarationSyntax;
24+
public static bool IsClass(this SyntaxNode node) => node is ClassDeclarationSyntax;
25+
26+
public static bool IsEnum(this SyntaxNode node) => node is EnumDeclarationSyntax;
2727

2828
public static bool IsRecordStruct(this SyntaxNode node)
2929
=> node is RecordDeclarationSyntax recordDecl && recordDecl.ClassOrStructKeyword.IsKind(SyntaxKind.StructKeyword);

src/Alimer.SourceGenerators/SerializationGenerator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public void Initialize(IncrementalGeneratorInitializationContext context)
1414
{
1515
IncrementalValuesProvider<ComponentType> classDeclarations = context.SyntaxProvider.ForAttributeWithMetadataName(
1616
fullyQualifiedMetadataName: AlimerTypes.DefaultEntitySystemAttribute,
17-
predicate: static (node, _) => node.IsClass() && node.IsPartial(),
17+
predicate: static (node, _) => node.IsPartial() && (node.IsClass() || node.IsStruct() || node.IsEnum()),
1818
transform: static (context, token) =>
1919
{
2020
INamedTypeSymbol classSymbol = (INamedTypeSymbol)context.TargetSymbol;

0 commit comments

Comments
 (0)