forked from LukasBanana/LLGL
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExample.metal
152 lines (122 loc) · 4.58 KB
/
Example.metal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Metal volume rendering shader
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
struct Settings
{
float4x4 wMatrix;
float4x4 wMatrixInv;
float4x4 vpMatrix;
float4x4 vpMatrixInv;
float4 lightDirAndShininess;
float4 viewPosAndThreshold; // threshold should be in open range (0, 0.5).
float4 albedoAndReflectance;
};
// VERTEX SHADER SCENE
struct VSceneIn
{
float3 position [[attribute(0)]];
float3 normal [[attribute(1)]];
};
struct VSceneOut
{
float4 position [[position]];
float4 ndc;
float4 worldPos;
float4 modelPos;
float4 normal;
};
vertex VSceneOut VScene(
VSceneIn inp [[stage_in]],
constant Settings& settings [[buffer(1)]])
{
VSceneOut outp;
outp.modelPos = float4(inp.position, 1);
outp.worldPos = settings.wMatrix * outp.modelPos;
outp.position = settings.vpMatrix * outp.worldPos;
outp.ndc = outp.position / outp.position.w;
outp.normal = settings.wMatrix * float4(inp.normal, 0);
return outp;
}
// PIXEL SHADER SCENE
float SampleNoise(texture3d<float> tex, sampler smpl, float3 v)
{
return tex.sample(smpl, v).r;
}
float SampleVolume(texture3d<float> tex, sampler smpl, float threshold, float3 pos)
{
float noise = SampleNoise(tex, smpl, pos*0.5);
return smoothstep(0.5 - threshold, 0.5 + threshold, noise);
}
float SampleVolumeInBoundary(texture3d<float> tex, sampler smpl, float threshold, float4x4 M, float3 pos)
{
return SampleVolume(tex, smpl, threshold, (M * float4(pos, 1)).xyz);
}
float DiffuseShading(float3 normal, float3 lightVec)
{
float NdotL = dot(normal, lightVec);
return mix(0.2, 1.0, max(0.0, NdotL));
}
float SpecularShading(float3 normal, float3 lightVec, float3 viewVec, float shininess)
{
float3 halfVec = normalize(viewVec + lightVec);
float NdotH = dot(normal, halfVec);
return pow(max(0.0, NdotH), shininess);
}
float Glitter(texture3d<float> tex, sampler smpl, float3 normal, float3 lightVec, float3 viewVec, float3 noisePos)
{
float RdotL = dot(reflect(-viewVec, normal), lightVec);
float specBase = mix(0.2, 1.0, saturate(RdotL));
float3 fp = fract(noisePos * 150.0 + SampleNoise(tex, smpl, noisePos * 12.0) * 9.0 + viewVec * 0.1);
fp *= (1.0 - fp);
float glitter = saturate(1.0 - 7.0 * (fp.x + fp.y + fp.z));
return glitter * pow(specBase, 1.5);
}
void UnprojectDepth(float4x4 M, thread float4& v)
{
v = M * v;
v /= v.w;
}
fragment float4 PScene(
VSceneOut inp [[stage_in]],
constant Settings& settings [[buffer(1)]],
texture3d<float> noiseTexture [[texture(2)]],
texture2d<float> depthRangeTexture [[texture(3)]],
sampler linearSampler [[sampler(4)]])
{
// Get input vectors
float3 normal = normalize(inp.normal.xyz);
float3 lightVec = -settings.lightDirAndShininess.xyz;
float3 viewVec = normalize(settings.viewPosAndThreshold.xyz - inp.worldPos.xyz);
// Compute blinn-phong shading
float diffuse = DiffuseShading(normal, lightVec);
float specular = SpecularShading(normal, lightVec, viewVec, settings.lightDirAndShininess.a) * settings.albedoAndReflectance.a;
// Sample noise texture and apply glitter
specular += Glitter(noiseTexture, linearSampler, normal, lightVec, viewVec, inp.worldPos.xyz);
// Project depth back into scene
float2 screenPos = inp.ndc.xy;
float2 texCoord = screenPos * float2(0.5, -0.5) + 0.5;
float maxDepth = depthRangeTexture.sample(linearSampler, texCoord).r;
float4 maxDepthPos = float4(screenPos.x, screenPos.y, maxDepth, 1.0);
UnprojectDepth(settings.vpMatrixInv, maxDepthPos);
// Sample volume density
int numIterations = 64;
float stride = 1.0 / (float)numIterations;
float trace = 0.0;
float density = 0.0;
for (int i = 0; i < numIterations; ++i)
{
float3 tracePos = mix(inp.worldPos.xyz, maxDepthPos.xyz, trace);
density += SampleVolumeInBoundary(noiseTexture, linearSampler, settings.viewPosAndThreshold.a, settings.wMatrixInv, tracePos) * stride;
trace += stride;
}
#if 0
// Apply density damping on the edges
float depthRange = distance(inp.worldPos.xyz, maxDepthPos.xyz);
if (depthRange > 5.0)
return 1.0;
//density *= smoothstep(0.1, 0.2, depthRange);
#endif
// Put everything together
return float4(settings.albedoAndReflectance.rgb * diffuse * mix(0.35, 1.5, density) + (float3)specular, 1.0);
}