 | [TOC] Chapter 10: Surface Properties |  |
 | Introduction |  |
Surface properties define how an object interacts with light, color, and textures. In WebGPU, using WGSL (WebGPU Shading Language), we can control surface appearance by adjusting color, texture, shading, and reflections. This chapter covers surface colors, smooth shading techniques like Phong shading, texture mapping using images and procedures, transformations on textures, and how to combine these surface properties with lighting to create realistic scenes.
Surface properties influence the final appearance of objects in 3D rendering, controlling color, texture, and reflectivity. By manipulating these properties, we can simulate various materials, from shiny metals to rough stone. Key concepts include how colors interact with lighting, smooth shading techniques like Phong shading, and texture mapping for adding detail.
 | Surface Colors |  |
Surface color defines an object’s base color, often used as a diffuse color in lighting calculations. Surface color can be specified as a single color or using a texture map that applies varying colors across a surface.
For example, applying a base color to a surface in WGSL:
@group(0) @binding(0) var<uniform> baseColor: vec3<f32>;
fn applyBaseColor() -> vec4<f32> { return vec4<f32>(baseColor, 1.0); }
In this example, `baseColor` is the uniform variable defining the surface's color, which can later be combined with lighting calculations.
 | Smooth Phong Shading |  |
Phong shading produces smooth gradients of color across surfaces by interpolating surface normals. This approach creates a visually smooth appearance even with low-polygon models, as shading is computed per pixel rather than per vertex.
Surface Normal Vectors
A normal vector is perpendicular to a surface at a given point and is essential for calculating how light interacts with the surface. In Phong shading, we interpolate normal vectors across a surface and then calculate lighting per pixel, which produces smooth transitions between lighting intensities.
Calculating Smooth Normal Vectors
To calculate smooth normals, we interpolate the normals of adjacent vertices. This blending smooths the shading across the surface, especially effective on curved surfaces.
Example vertex shader in WGSL, calculating interpolated normals:
struct VertexInput { @location(0) position: vec3<f32>, @location(1) normal: vec3<f32>, };
struct VertexOutput { @builtin(position) position: vec4<f32>, @location(0) fragNormal: vec3<f32>, };
@vertex fn vertex_main(input: VertexInput) -> VertexOutput { var output: VertexOutput; output.position = vec4<f32>(input.position, 1.0); output.fragNormal = normalize(input.normal); return output; }
In the fragment shader, smooth shading is applied by using the interpolated normal to calculate lighting:
@fragment fn fragment_main(@location(0) fragNormal: vec3<f32>) -> @location(0) vec4<f32> { let lightDir = normalize(vec3<f32>(0.5, 1.0, 0.5)); let diffIntensity = max(dot(fragNormal, lightDir), 0.0); let diffuseColor = vec3<f32>(0.8, 0.6, 0.3) * diffIntensity; return vec4<f32>(diffuseColor, 1.0); }
 | Texture Mapping Using Images |  |
Texture mapping is the process of applying an image to a 3D surface, giving it a more detailed appearance. Texture images are loaded and bound in WebGPU and accessed in the fragment shader to determine the color for each pixel.
Example of a simple WGSL fragment shader using an image texture:
@group(0) @binding(1) var myTexture: texture_2d<f32>; @group(0) @binding(2) var mySampler: sampler;
@fragment fn fragment_main(@location(0) texCoords: vec2<f32>) -> @location(0) vec4<f32> { return textureSample(myTexture, mySampler, texCoords); }
 | Texture Mapping - Texture Coordinates |  |
Texture coordinates, also known as UV coordinates, map each vertex on a 3D object to a point on a 2D texture. The coordinates range from (0,0) in the bottom-left corner of the texture to (1,1) in the top-right corner.
Example vertex shader passing UV coordinates:
struct VertexInput { @location(0) position: vec3<f32>, @location(1) texCoord: vec2<f32>, };
struct VertexOutput { @builtin(position) position: vec4<f32>, @location(0) fragTexCoord: vec2<f32>, };
@vertex fn vertex_main(input: VertexInput) -> VertexOutput { var output: VertexOutput; output.position = vec4<f32>(input.position, 1.0); output.fragTexCoord = input.texCoord; return output; }
 | Texture Mapping Using Procedures |  |
Procedural textures generate patterns algorithmically, such as checkerboards, noise, or gradients, rather than loading image files. They are flexible and memory-efficient since they don't require loading large image files.
Example WGSL fragment shader creating a checkerboard pattern:
@fragment fn fragment_main(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> { let checker = mod(floor(texCoord.x * 10.0) + floor(texCoord.y * 10.0), 2.0); let color = mix(vec3<f32>(1.0, 1.0, 1.0), vec3<f32>(0.0, 0.0, 0.0), checker); return vec4<f32>(color, 1.0); }
This creates a 10x10 checkerboard pattern by varying colors based on the floor function of scaled UV coordinates.
Texture Mapping Using Procedures - Continued
Procedural textures can include complex noise-based patterns for natural effects like marble or wood. This can be achieved using functions like Perlin or Simplex noise, which generate smooth, randomized patterns.
 | Transformations on Texture Maps |  |
Texture coordinates can be manipulated to transform the texture on the surface. Scaling, rotating, or translating the UV coordinates will scale, rotate, or translate the texture itself.
For example, scaling UV coordinates to repeat a texture:
@fragment fn fragment_main(@location(0) texCoord: vec2<f32>) -> @location(0) vec4<f32> { let scaledTexCoord = texCoord * vec2<f32>(4.0, 4.0); // 4x repetition return textureSample(myTexture, mySampler, scaledTexCoord); }
 | Other Surface Manipulations |  |
Beyond colors and textures, surface properties can include bump maps (for simulating surface irregularities), normal maps (for more realistic light interactions), and displacement maps (to alter the geometry based on texture data). Each of these adds depth and realism to surface appearance.
Example normal map application in WGSL:
@group(0) @binding(3) var normalMap: texture_2d<f32>;
@fragment fn fragment_main(@location(0) texCoord: vec2<f32>, @location(1) normal: vec3<f32>) -> @location(0) vec4<f32> { let normalFromMap = textureSample(normalMap, mySampler, texCoord).xyz * 2.0 - 1.0; let perturbedNormal = normalize(normal + normalFromMap); // Apply lighting with perturbed normal let lightDir = normalize(vec3<f32>(1.0, 1.0, 1.0)); let diffIntensity = max(dot(perturbedNormal, lightDir), 0.0); let color = vec3<f32>(0.8, 0.6, 0.3) * diffIntensity; return vec4<f32>(color, 1.0); }
 | Combining Light and Surface Properties |  |
For realistic rendering, we combine lighting and surface properties. This includes calculating lighting (diffuse, specular, ambient) and blending it with the base color or texture, considering any perturbations from normal or bump maps.
Example shader combining ambient, diffuse, and specular lighting with a texture:
@fragment fn fragment_main(@location(0) texCoord: vec2<f32>, @location(1) fragNormal: vec3<f32>) -> @location(0) vec4<f32> { let ambientStrength = 0.1; let lightDir = normalize(vec3<f32>(0.5, 1.0, 0.5)); let viewDir = normalize(vec3<f32>(0.0, 0.0, 1.0)); let color = textureSample(myTexture, mySampler, texCoord).rgb; let ambient = color * ambientStrength;
let diff = max(dot(fragNormal, lightDir), 0.0); let diffuse = diff * color;
let reflectDir = reflect(-lightDir, fragNormal); let spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); let specular = vec3<f32>(1.0, 1.0, 1.0) * spec * 0.5
;
let finalColor = ambient + diffuse + specular; return vec4<f32>(finalColor, 1.0); }
 | Summary |  |
In this chapter, we explored surface properties and how they affect rendering. We covered:
• Surface colors and their role in basic rendering.
• Smooth Phong shading to produce soft gradients across surfaces.
• Texture mapping with images and procedural patterns to add detail.
• Texture transformations for flexibility in applying textures.
• Advanced surface manipulations like normal mapping to add realism.
• Combining these properties with lighting calculations for high-quality 3D rendering.
Mastering these techniques, you can simulate various materials and surface details, achieving a more immersive and visually compelling 3D scene.
|