@@ -63,6 +63,30 @@ struct PBRData {
63
63
specular: vec3<f32>,
64
64
}
65
65
66
+ struct Material {
67
+ ior: f32,
68
+ roughness: f32,
69
+ metallic: f32,
70
+ base: vec3<f32>,
71
+ };
72
+
73
+ struct DirectionalLight {
74
+ direction: vec3<f32>,
75
+ color: vec3<f32>,
76
+ };
77
+
78
+ struct PointLight {
79
+ position: vec3<f32>,
80
+ color: vec3<f32>,
81
+ };
82
+
83
+ struct SpotLight {
84
+ position: vec3<f32>,
85
+ direction: vec3<f32>,
86
+ cones: vec2<f32>,
87
+ color: vec3<f32>,
88
+ };
89
+
66
90
const pi: f32 = 3.14159265359;
67
91
68
92
// Dot product with the max already in it
@@ -167,53 +191,51 @@ fn cookTorrance(D: f32, F: f32, G: f32, N: vec3<f32>, V: vec3<f32>, L: vec3<f32>
167
191
}
168
192
169
193
// Different lighting calculations for different light sources
170
- fn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, ior: f32, roughness: f32, metallic: f32, direction: vec3<f32>, color: vec3<f32>, base: vec3<f32> ) -> PBRData {
171
- var L: vec3<f32> = normalize(direction); // Light Vector
194
+ fn calcDirectionalLight(N: vec3<f32>, V: vec3<f32>, mat: Material, light: DirectionalLight ) -> PBRData {
195
+ var L: vec3<f32> = normalize(light. direction); // Light Vector
172
196
var H: vec3<f32> = normalize(L + V); // Halfway Vector
173
197
174
- var alpha = roughness* roughness;
175
- var k: f32 = alpha* alpha / 2;
198
+ var alpha = mat. roughness * mat. roughness;
199
+ var k: f32 = alpha * alpha / 2.0 ;
176
200
177
201
var D: f32 = trGGX(N, H, alpha); // Distribution
178
202
// var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel
179
203
var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry
180
204
181
205
var brdf: f32 = cookTorrance(D, 1.0, G, N, V, L); // Fresnel term is replaced with 1 because it is added later
182
- var incoming: vec3<f32> = color;
206
+ var incoming: vec3<f32> = light. color;
183
207
var angle: f32 = mdot(L, N);
184
208
angle = pow(angle, 1.5);
185
209
186
- var specular: vec3<f32> = brdf* incoming* angle;
210
+ var specular: vec3<f32> = brdf * incoming * angle;
187
211
// Oren-Nayar gives a clay-like effect when fully rough which some people may not want, so it might be better to give a separate
188
212
// control property for the diffuse vs specular roughness
189
- var diffuse: vec3<f32> = incoming* fujiiOrenNayar(base, roughness, N, L, V);
213
+ var diffuse: vec3<f32> = incoming * fujiiOrenNayar(mat. base, mat. roughness, N, L, V);
190
214
// Stores the specular and diffuse separately to allow for finer post processing
191
215
var out = PBRData(diffuse, specular);
192
216
193
217
return out; // Returns angle along with color of light so the final color can be multiplied by angle as well (creates black areas)
194
218
}
195
219
196
- // TODO: find some way to reduce the number of arguments going in here
197
- fn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, color: vec3<f32>, base: vec3<f32>) -> PBRData {
198
- var L: vec3<f32> = normalize(position - fragPos); // Light Vector
199
- var H: vec3<f32> = normalize(L + V); // Halfway Vector
200
- var dist = distance(position, fragPos);
220
+ fn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, mat: Material, light: PointLight) -> PBRData {
221
+ var L: vec3<f32> = normalize(light.position - fragPos);
222
+ var H: vec3<f32> = normalize(L + V);
223
+ var dist = distance(light.position, fragPos);
201
224
202
- var alpha = roughness* roughness;
203
- var k: f32 = alpha* alpha / 2.0; // could also be pow(alpha + 1.0, 2) / 8
225
+ var alpha = mat. roughness * mat. roughness;
226
+ var k: f32 = alpha * alpha / 2.0;
204
227
205
228
var D: f32 = trGGX(N, H, alpha); // Distribution
206
- // var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel
229
+ var F: f32 = schlickFresnelIOR(V, N, mat. ior, k); // Fresnel
207
230
var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry
208
231
209
232
var brdf: f32 = cookTorrance(D, 1.0, G, N, V, L);
210
- var incoming: vec3<f32> = color * (1.0 / (dist* dist));
233
+ var incoming: vec3<f32> = light. color * (1.0 / (dist * dist));
211
234
var angle: f32 = mdot(L, N);
212
- angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly "seams" bewteen light sources
213
-
214
- var specular: vec3<f32> = brdf*incoming*angle;
215
- var diffuse: vec3<f32> = incoming*fujiiOrenNayar(base, roughness, N, L, V);
235
+ angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly "seams" between light sources
216
236
237
+ var specular: vec3<f32> = brdf * incoming * angle;
238
+ var diffuse: vec3<f32> = incoming * fujiiOrenNayar(mat.base, mat.roughness, N, L, V);
217
239
// Stores the specular and diffuse separately to allow for finer post processing
218
240
// Could also be done (propably more properly) with a struct
219
241
var out = PBRData(diffuse, specular);
@@ -222,34 +244,33 @@ fn calcPointLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roug
222
244
}
223
245
224
246
// For a reason unknown to me, spheres dont seem to behave propperly with head-on spot lights
225
- fn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, ior: f32, roughness: f32, metallic: f32, position: vec3<f32>, direction: vec3<f32>, cones: vec2<f32>, color: vec3<f32>, base: vec3<f32> ) -> PBRData {
226
- var L: vec3<f32> = normalize(position - fragPos);
247
+ fn calcSpotLight(N: vec3<f32>, V: vec3<f32>, fragPos: vec3<f32>, mat: Material, light: SpotLight ) -> PBRData {
248
+ var L: vec3<f32> = normalize(light. position - fragPos);
227
249
var H: vec3<f32> = normalize(L + V); // Halfway Vector
228
- var dist = distance(position, fragPos);
250
+ var dist = distance(light. position, fragPos);
229
251
230
- var alpha = roughness* roughness;
231
- var k: f32 = alpha* alpha / 2.0; // could also be pow(alpha + 1.0, 2) / 8
252
+ var alpha = mat. roughness * mat. roughness;
253
+ var k: f32 = alpha * alpha / 2.0; // could also be pow(alpha + 1.0, 2) / 8
232
254
233
255
var D: f32 = trGGX(N, H, alpha); // Distribution
234
256
// var F: f32 = schlickFresnelIOR(V, N, ior, k); // Fresnel
235
257
var G: f32 = smithSurfaceRoughness(N, V, L, k); // Geometry
236
258
237
259
var brdf: f32 = cookTorrance(D, 1.0, G, N, V, L);
238
260
239
- // Cones.x is the inner phi and cones.y is the outer phi
240
- var theta: f32 = mdot(normalize(direction), L);
241
- var epsilon: f32 = cones.x - cones.y;
242
- var intensity: f32 = (theta - cones.y) / epsilon;
261
+ var theta: f32 = mdot(normalize(light.direction), L);
262
+ var epsilon: f32 = light.cones.x - light.cones.y;
263
+ var intensity: f32 = (theta - light.cones.y) / epsilon;
243
264
intensity = clamp(intensity, 0.0, 1.0);
244
- intensity /= dist* dist;
265
+ intensity /= dist * dist;
245
266
246
- var incoming: vec3<f32> = color * intensity;
267
+ var incoming: vec3<f32> = light. color * intensity;
247
268
248
269
var angle: f32 = mdot(L, N);
249
- angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly "seams" bewteen light sources
270
+ angle = pow(angle, 1.5); // Smoothing factor makes it less accurate, but reduces ugly "seams" between light sources
250
271
251
- var specular: vec3<f32> = brdf* incoming* angle;
252
- var diffuse: vec3<f32> = incoming* fujiiOrenNayar(base, roughness, N, L, V);
272
+ var specular: vec3<f32> = brdf * incoming * angle;
273
+ var diffuse: vec3<f32> = incoming * fujiiOrenNayar(mat. base, mat. roughness, N, L, V);
253
274
254
275
// Stores the specular and diffuse separately to allow for finer post processing
255
276
// Could also be done (propably more properly) with a struct
@@ -302,6 +323,7 @@ fn main(
302
323
var ambientColor: vec4<f32> = mapperUBO.AmbientColor;
303
324
var diffuseColor: vec4<f32> = mapperUBO.DiffuseColor;
304
325
var opacity: f32 = mapperUBO.Opacity;
326
+ var ior: f32 = mapperUBO.BaseIOR;
305
327
306
328
// This should be declared somewhere else
307
329
var _diffuseMap: vec4<f32> = vec4<f32>(1.0);
@@ -631,89 +653,89 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
631
653
) {
632
654
const lightingCode = [
633
655
// Vectors needed for light calculations
634
- ' var fragPos: vec3<f32> = vec3<f32>(input.vertexVC.xyz);' ,
635
- ' var V: vec3<f32> = mix(normalize(-fragPos), vec3<f32>(0, 0, 1), f32(rendererUBO.cameraParallel)); // View Vector' ,
656
+ ' let fragPos = vec3<f32>(input.vertexVC.xyz);' ,
657
+ ' let V = mix(normalize(-fragPos), vec3<f32>(0, 0, 1), f32(rendererUBO.cameraParallel)); // View Vector' ,
636
658
// Values needed for light calculations
637
- ' var baseColor: vec3<f32> = _diffuseMap.rgb * diffuseColor.rgb;' ,
638
- ' var roughness: f32 = max(0.000001, mapperUBO.Roughness * _roughnessMap.r);' , // Need to have a different way of sampling greyscale values aside from .r
639
- ' var metallic: f32 = mapperUBO.Metallic * _metallicMap.r;' ,
640
- ' var alpha: f32 = roughness*roughness;' ,
641
- ' var ior: f32 = mapperUBO.BaseIOR;' ,
642
- ' var k: f32 = alpha*alpha / 2;' ,
659
+ ' let baseColor = _diffuseMap.rgb * diffuseColor.rgb;' ,
660
+ ' let roughness = max(0.000001, mapperUBO.Roughness * _roughnessMap.r);' , // Need to have a different way of sampling greyscale values aside from .r
661
+ ' let metallic = mapperUBO.Metallic * _metallicMap.r;' ,
662
+ ' let alpha = roughness * roughness;' ,
663
+ ' let k = alpha * alpha / 2.0;' ,
643
664
// Split diffuse and specular components
644
- ' var diffuse: vec3<f32> = vec3<f32>(0.);' ,
645
- ' var specular: vec3<f32> = vec3<f32>(0.);' ,
646
- ' var emission: vec3<f32> = _emissionMap.rgb * mapperUBO.Emission;' ,
665
+ ' var diffuse = vec3<f32>(0.);' ,
666
+ ' var specular = vec3<f32>(0.);' ,
667
+ ' let emission = _emissionMap.rgb * mapperUBO.Emission;' ,
668
+ '' ,
669
+ ' // Material struct' ,
670
+ ' let mat = Material(ior, roughness, metallic, baseColor);' ,
671
+ '' ,
647
672
// Summing diffuse and specular components of directional lights
648
673
' {' ,
649
- ' var i: i32 = 0;' ,
674
+ ' var i = 0;' ,
650
675
' loop {' ,
651
- ' if !(i < rendererUBO.LightCount) { break; }' ,
676
+ ' if ( !(i < rendererUBO.LightCount) ) { break; }' ,
652
677
' switch (i32(rendererLightSSBO.values[i].LightData.x)) {' ,
653
678
' // Point Light' ,
654
679
' case 0 {' ,
655
- ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;' ,
656
- ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;' ,
657
- ' var calculated: PBRData = calcPointLight(normal, V, fragPos, ior, roughness, metallic, pos, color, baseColor);' ,
658
- ' diffuse += max(vec3<f32>(0), calculated.diffuse);' ,
659
- ' specular += max(vec3<f32>(0), calculated.specular);' ,
680
+ ' let color = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;' ,
681
+ ' let pos = (rendererLightSSBO.values[i].LightPos).xyz;' ,
682
+ ' let pointLight = PointLight(pos, color);' ,
683
+ ' let result = calcPointLight(normal, V, fragPos, mat, pointLight);' ,
684
+ ' diffuse += max(vec3<f32>(0), result.diffuse);' ,
685
+ ' specular += max(vec3<f32>(0), result.specular);' ,
660
686
' }' ,
661
687
' // Directional light' ,
662
688
' case 1 {' ,
663
- ' var dir: vec3<f32> = ( rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;' ,
664
- ' dir = normalize(dir) ;' ,
665
- ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w ;' ,
666
- ' var calculated: PBRData = calcDirectionalLight(normal, V, ior, roughness, metallic, dir, color, baseColor ); // diffuseColor.rgb needs to be fixed with a more dynamic diffuse color' ,
667
- ' diffuse += max(vec3<f32>(0), calculated .diffuse);' ,
668
- ' specular += max(vec3<f32>(0), calculated .specular);' ,
689
+ ' let dir = normalize(( rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz) ;' ,
690
+ ' let color = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w ;' ,
691
+ ' let dirLight = DirectionalLight(dir, color) ;' ,
692
+ ' let result = calcDirectionalLight(normal, V, mat, dirLight ); // diffuseColor.rgb needs to be fixed with a more dynamic diffuse color' ,
693
+ ' diffuse += max(vec3<f32>(0), result .diffuse);' ,
694
+ ' specular += max(vec3<f32>(0), result .specular);' ,
669
695
' }' ,
670
696
' // Spot Light' ,
671
697
' case 2 {' ,
672
- ' var color: vec3<f32> = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;' ,
673
- ' var pos: vec3<f32> = (rendererLightSSBO.values[i].LightPos).xyz;' ,
674
- ' var dir: vec3<f32> = ( rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz;' ,
675
- ' dir = normalize(dir );' ,
676
- ' var cones: vec2<f32> = vec2<f32>(rendererLightSSBO.values[i].LightData.y, rendererLightSSBO.values[i].LightData.z );' ,
677
- ' var calculated: PBRData = calcSpotLight(normal, V, fragPos, ior, roughness, metallic, pos, dir, cones, color, baseColor );' ,
678
- ' diffuse += max(vec3<f32>(0), calculated .diffuse);' ,
679
- ' specular += max(vec3<f32>(0), calculated .specular);' ,
698
+ ' let color = rendererLightSSBO.values[i].LightColor.rgb * rendererLightSSBO.values[i].LightColor.w;' ,
699
+ ' let pos = (rendererLightSSBO.values[i].LightPos).xyz;' ,
700
+ ' let dir = normalize(( rendererUBO.WCVCNormals * vec4<f32>(normalize(rendererLightSSBO.values[i].LightDir.xyz), 0.)).xyz) ;' ,
701
+ ' let cones = vec2<f32>(rendererLightSSBO.values[i].LightData.y, rendererLightSSBO.values[i].LightData.z );' ,
702
+ ' let spotLight = SpotLight(pos, dir, cones, color );' ,
703
+ ' let result = calcSpotLight(normal, V, fragPos, mat, spotLight );' ,
704
+ ' diffuse += max(vec3<f32>(0), result .diffuse);' ,
705
+ ' specular += max(vec3<f32>(0), result .specular);' ,
680
706
' }' ,
681
707
' default { continue; }' ,
682
708
' }' ,
683
709
' continuing { i++; }' ,
684
710
' }' ,
685
711
' }' ,
686
712
// Final variables for combining specular and diffuse
687
- ' var fresnel: f32 = schlickFresnelIOR(V, normal, ior, k); // Fresnel' ,
688
- ' fresnel = min(1.0, fresnel);' ,
713
+ ' let fresnel = min(1.0, schlickFresnelIOR(V, normal, ior, k)); // Fresnel' ,
689
714
' // This could be controlled with its own variable (that isnt base color) for better artistic control' ,
690
- ' var fresnelMetallic: vec3<f32> = schlickFresnelRGB(V, normal, baseColor); // Fresnel for metal, takes color into account' ,
691
- ' var kS: vec3<f32> = mix(vec3<f32>(fresnel), fresnelMetallic, metallic);' ,
692
- ' kS = min(vec3<f32>(1.0), kS);' ,
693
- ' var kD: vec3<f32> = (1.0 - kS) * (1.0 - metallic);' ,
694
- ' var PBR: vec3<f32> = mapperUBO.DiffuseIntensity*kD*diffuse + kS*specular;' ,
695
- ' PBR += emission;' ,
696
- ' computedColor = vec4<f32>(PBR, mapperUBO.Opacity);' ,
715
+ ' let fresnelMetallic = schlickFresnelRGB(V, normal, baseColor); // Fresnel for metal, takes color into account' ,
716
+ ' let kS = min(vec3<f32>(1.0), mix(vec3<f32>(fresnel), fresnelMetallic, metallic));' ,
717
+ ' let kD = (1.0 - kS) * (1.0 - metallic);' ,
718
+ ' let PBR = mapperUBO.DiffuseIntensity * kD * diffuse + kS * specular;' ,
719
+ ' computedColor = vec4<f32>(PBR + emission, mapperUBO.Opacity);' ,
697
720
] ;
698
721
if ( renderer . getEnvironmentTexture ( ) ?. getImageLoaded ( ) ) {
699
722
lightingCode . push (
700
723
' // To get diffuse IBL, the texture is sampled with normals in worldspace' ,
701
- ' var diffuseIBLCoords: vec3<f32> = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(normal, 1.)).xyz;' ,
702
- ' var diffuseCoords: vec2<f32> = vecToRectCoord(diffuseIBLCoords);' ,
724
+ ' let diffuseIBLCoords = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(normal, 1.)).xyz;' ,
725
+ ' let diffuseCoords = vecToRectCoord(diffuseIBLCoords);' ,
703
726
' // To get specular IBL, the texture is sampled as the worldspace reflection between the normal and view vectors' ,
704
727
' // Reflections are first calculated in viewspace, then converted to worldspace to sample the environment' ,
705
- ' var VreflN: vec3<f32> = normalize(reflect(-V, normal));' ,
706
- ' var reflectionIBLCoords = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(VreflN, 1.)).xyz;' ,
707
- ' var specularCoords: vec2<f32> = vecToRectCoord(reflectionIBLCoords);' ,
708
- ' var diffuseIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, diffuseCoords, rendererUBO.MaxEnvironmentMipLevel);' ,
728
+ ' let VreflN = normalize(reflect(-V, normal));' ,
729
+ ' let reflectionIBLCoords = (transpose(rendererUBO.WCVCNormals) * vec4<f32>(VreflN, 1.)).xyz;' ,
730
+ ' let specularCoords = vecToRectCoord(reflectionIBLCoords);' ,
731
+ ' let diffuseIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, diffuseCoords, rendererUBO.MaxEnvironmentMipLevel);' ,
709
732
// Level multiplier should be set by UBO
710
- ' var level = roughness * rendererUBO.MaxEnvironmentMipLevel;' ,
711
- ' var specularIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, specularCoords, level);' , // Manual mip smoothing since not all formats support smooth level sampling
712
- ' var specularIBLContribution: vec3<f32> = specularIBL.rgb*rendererUBO.BackgroundSpecularStrength;' ,
713
- ' computedColor += vec4<f32>(specularIBLContribution*kS, 0);' ,
714
- ' var diffuseIBLContribution: vec3<f32> = diffuseIBL.rgb*rendererUBO.BackgroundDiffuseStrength;' ,
715
- ' diffuseIBLContribution *= baseColor * _ambientOcclusionMap.rgb;' , // Multipy by baseColor may be changed
716
- ' computedColor += vec4<f32>(diffuseIBLContribution*kD, 0);'
733
+ ' let level = roughness * rendererUBO.MaxEnvironmentMipLevel;' ,
734
+ ' let specularIBL = textureSampleLevel(EnvironmentTexture, EnvironmentTextureSampler, specularCoords, level);' , // Manual mip smoothing since not all formats support smooth level sampling
735
+ ' let specularIBLContribution = specularIBL.rgb * rendererUBO.BackgroundSpecularStrength;' ,
736
+ ' computedColor += vec4<f32>(specularIBLContribution * kS, 0);' ,
737
+ ' let diffuseIBLContribution = diffuseIBL.rgb * rendererUBO.BackgroundDiffuseStrength;' ,
738
+ ' computedColor += vec4<f32>(diffuseIBLContribution * baseColor * _ambientOcclusionMap.rgb * kD, 0);'
717
739
) ;
718
740
}
719
741
code = vtkWebGPUShaderCache . substitute (
@@ -725,8 +747,8 @@ function vtkWebGPUCellArrayMapper(publicAPI, model) {
725
747
// If theres no normals, just set the specular color to be flat
726
748
} else {
727
749
code = vtkWebGPUShaderCache . substitute ( code , '//VTK::Light::Impl' , [
728
- ' var diffuse: vec3<f32> = diffuseColor.rgb;' ,
729
- ' var specular: vec3<f32> = mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;' ,
750
+ ' let diffuse = diffuseColor.rgb;' ,
751
+ ' let specular = mapperUBO.SpecularColor.rgb * mapperUBO.SpecularColor.a;' ,
730
752
' computedColor = vec4<f32>(diffuse * _diffuseMap.rgb, mapperUBO.Opacity);' ,
731
753
] ) . result ;
732
754
fDesc . setCode ( code ) ;
0 commit comments