3D Cameras, Lighting Models, Materials & Shaders¶
Summary¶
Controls 3D cameras, ambient/directional/point lights, specular materials, texture maps, OBJ models, and GLSL shaders. Students will gain practical hands-on experience by building interactive sketches and visual experiments that demonstrate these concepts.
Concepts Covered¶
This chapter covers the following 20 concepts from the learning graph:
- Ambient Light Source
- Directional Light Source
- Point Light Source
- Spot Light Source
- Normal Material Shading
- Basic Material Shading
- Ambient Material Shading
- Specular Material Shading
- Shininess Parameter
- Texture Mapping Function
- Create Shader Function
- Load Shader p5 Files
- Shader Uniform Variables
- Fragment Shader Filter
- Vertex Shader Displacement
- 3D Model OBJ Import
- Load Model Function
- 3D Mesh Vertex Array
- 3D Lighting Model Combined
- Z Buffer Depth Test
Prerequisites¶
This chapter builds on concepts from:
Welcome to the 3D Stage!

Welcome to the 3D stage, creators! Having 3D shapes is neat, but it's the cinematic lighting and materials that make them look truly spectacular. Dive into this chapter to learn how to direct your virtual cameras and light up your scenes like a pro!
Imagine you are a movie director. You have an empty stage. It is completely pitch black. To make a movie, you need characters, you need materials for their costumes, you need cameras to capture the action, and most importantly, you need lights. Without lights, everything remains hidden in the void. In p5.js, working in WebGL mode is exactly like being a movie director on a digital stage. You are responsible for placing the objects, moving the camera, and setting up the stage lights.
The Movie Director and Stage Lights¶
When you create a 3D scene, you are basically writing the script for how the computer should draw shapes and illuminate them. This process involves a lot of math, but don't worry, p5.js handles the heavy lifting. All you have to do is make the creative choices.
Let's start with lighting. In the real world, light bounces off surfaces and enters our eyes. In a computer simulation, we have to calculate how light interacts with every single pixel on the screen. To make things manageable, we break lighting down into different types of light sources.
The first and most basic type is the Ambient Light Source. Think of ambient light as the baseline level of brightness in a room. It doesn't come from any specific direction, and it doesn't create any shadows. It just makes everything uniformly visible. If you only use an Ambient Light Source, your 3D objects will look flat, like 2D cutouts, because there are no highlights or shadows to give them a sense of volume.
To add depth, we need lights that have direction. A Directional Light Source is like the sun. It's so far away that its light rays are essentially parallel by the time they reach your scene. It hits all objects from the same angle. When you add a Directional Light Source, suddenly your spheres look round and your boxes have distinct sides.
If you want a light that acts like a lightbulb in the middle of a room, you need a Point Light Source. This type of light emanates from a specific point in 3D space and spreads out in all directions. Objects that are closer to the Point Light Source will appear brighter, and objects further away will appear dimmer.
For even more dramatic lighting, like a flashlight or a theatrical spotlight, you use a Spot Light Source. A Spot Light Source is like a point light, but its light is restricted to a cone shape. You can control the angle of the cone and the direction it's pointing. This is perfect for highlighting specific objects on your stage.
Lighting Strategy
Notice how lighting in 3D isn't actually "light" at all—it's just a math equation! We are calculating the angle between a light ray and the surface of our geometry to determine what color the pixel should be. You're painting with geometry!
Dressing the Set: Materials¶
Once you have lights, you need to decide how your objects react to those lights. In 3D graphics, this is called the material. A material defines the color, texture, and shininess of an object's surface.
If you want an object to be completely unaffected by lights, you can use Normal Material Shading. This is a special type of material that colors the object based on the direction its surface is facing. It's often used for debugging, so you can easily see the geometry of your shapes regardless of the lighting setup.
For simple, unlit objects, you can use Basic Material Shading. This just gives the object a solid color, ignoring any lights in the scene.
But if you want your objects to interact with the lights you've so carefully placed, you need to use more advanced materials. Ambient Material Shading determines how much of the Ambient Light Source the object reflects.
For shiny objects like plastic, metal, or wet surfaces, you need Specular Material Shading. This calculates the bright highlights that appear when light reflects directly into the camera. The size and sharpness of these highlights are controlled by the Shininess Parameter. A high Shininess Parameter will create a small, sharp highlight, like on a billiard ball. A low Shininess Parameter will create a large, soft highlight, like on a piece of chalk.
Material Matters
Want your shapes to look like shiny, wet plastic? Call specularMaterial(255) followed by a high shininess(100) right before you draw your geometry!
Sometimes, a solid color isn't enough. What if you want your object to look like it's made of wood, brick, or even have a picture painted on it? That's where the Texture Mapping Function comes in. Texture mapping takes a 2D image and wraps it around a 3D object, much like wrapping paper on a present. You can use any image as a texture, and p5.js will calculate how to stretch and pin it to the surface of your geometry.
Shaders: The Ultimate Control¶
Lighting and materials are great, but sometimes you need absolute control over how every single pixel is drawn. For that, you need to write your own shaders. Shaders are small programs that run directly on the graphics processing unit (GPU). They are written in a language called GLSL (OpenGL Shading Language).
There are two main types of shaders: vertex shaders and fragment shaders. The vertex shader processes every single point (vertex) in your 3D geometry. The fragment shader processes every single pixel (fragment) that makes up the surface of the geometry.
To use custom shaders in p5.js, you first need to write them in separate files (usually ending in .vert and .frag). Then, you use the Load Shader p5 Files function to bring them into your sketch. Once loaded, you activate them using the Create Shader Function.
Shaders are incredibly powerful but can be tricky to learn. The p5.js sketch acts as the director, sending instructions and data to the shaders. These pieces of data are called Shader Uniform Variables. You can pass numbers, vectors, colors, and even images to your shaders using Shader Uniform Variables. The shader then uses this data to perform its calculations.
For example, you could write a Fragment Shader Filter that takes an image and applies a blur, color tint, or edge detection effect. Or, you could write a Vertex Shader Displacement that moves the vertices of a 3D model up and down based on the bright and dark areas of a texture map, creating realistic bumps and wrinkles.
Shader Magic
If GLSL and vertex math feel completely overwhelming right now, take a deep breath—that's completely normal! Shaders are notoriously tricky, but just like when you learned for loops, start by tweaking one number at a time and watch what happens. You've got this!
Importing the Cast: 3D Models¶
While p5.js has built-in functions for drawing spheres, boxes, and cylinders, sometimes you want to bring in more complex shapes, like a character or a vehicle. You can do this by creating your models in a 3D modeling program like Blender and exporting them as an OBJ file.
An OBJ file is a standard text format that lists all the vertices, texture coordinates, and faces that make up a 3D model. This is called 3D Model OBJ Import.
To use an OBJ file in your sketch, you use the Load Model Function. This reads the file and creates a p5.Geometry object. Inside this object is a 3D Mesh Vertex Array, which is simply a long list of all the points that define the shape.
Once the model is loaded, you can treat it just like any of the built-in shapes. You can apply materials, add lights, and even pass it to your custom shaders.
Putting it all Together¶
When you combine all these elements—cameras, lights, materials, textures, shaders, and imported models—you get the 3D Lighting Model Combined. This is the complete system that calculates the final color of every pixel on your screen.
As the director, you also have to manage the staging. When you draw multiple objects in 3D space, the computer needs to know which objects are in front of others. This is handled automatically by the Z Buffer Depth Test. The Z buffer keeps track of the depth (Z-coordinate) of every pixel drawn. Before drawing a new pixel, the graphics card checks the Z buffer to see if there is already something closer to the camera at that position. If there is, the new pixel is ignored. If not, the new pixel is drawn, and its depth is recorded in the Z buffer.
Bravo! Cut! Print!
Incredible work! You just mastered ambient and specular lighting, texture mapping, and even wrote your own GLSL shaders. You are a true director of the digital stage!
Diagram: Stage Lighting Setup¶
Run Stage Lighting Setup Fullscreen
MicroSim: Stage Lighting Setup
MicroSim: Stage Lighting Setup
Goal: Allow students to interactively place lights on a 3D stage and observe how different materials react.
UI Elements: - Sliders for Ambient, Directional, Point, and Spot Light Source intensities. - Dropdown to select the material (Normal, Basic, Ambient, Specular) for the central object. - Slider for Shininess Parameter.
Behavior: - The scene contains a rotating sphere in the center. - As the user adjusts the light sliders, the illumination on the sphere changes dynamically. - When a new material is selected, the sphere's appearance updates to reflect the chosen shading model.
Diagram: Exploring Shaders¶
Run Exploring Shaders Fullscreen
MicroSim: Exploring Shaders
MicroSim: Exploring Shaders
Goal: Demonstrate the power of GLSL shaders for real-time visual effects.
UI Elements: - A file upload button for Load Shader p5 Files. - Sliders mapped to Shader Uniform Variables (e.g., time, mouse position, color tint). - A toggle switch to switch between a Fragment Shader Filter and a Vertex Shader Displacement.
Behavior: - The canvas displays a 3D plane or sphere. - When the user uploads a valid shader, it's applied to the geometry. - Adjusting the uniform sliders alters the visual output of the shader in real-time, demonstrating how the p5.js sketch communicates with the GPU.
Deep Dive: The Physics of Light¶
To truly master our digital stage, we must understand the physical properties of light that we are simulating. Real-world light is a form of electromagnetic radiation. When light from a source like the sun or a lightbulb hits an object, several things can happen. It can be absorbed, transmitted, or reflected.
In our 3D computer graphics models, we primarily care about reflection. There are two main types of reflection: diffuse and specular. Diffuse reflection happens when light hits a rough surface and scatters in all directions. This is what gives an object its base color and makes it visible regardless of the camera's position. In p5.js, this is simulated using ambient and directional lights combined with ambient and basic materials.
Specular reflection, on the other hand, happens when light hits a smooth, shiny surface and bounces off in a concentrated beam, much like a mirror. This creates the bright highlights we see on polished metal or wet objects. The Shininess Parameter in our specular materials controls the microscopic roughness of the surface, determining whether the specular highlight is sharp and small (smooth surface) or wide and soft (rougher surface).
Understanding these physical principles allows you, the director, to make informed decisions about how to light your scene to achieve the desired mood and realism. By carefully balancing the different types of light sources and materials, you can create scenes that range from cartoonish and flat to highly realistic and atmospheric.
Advanced Shader Techniques¶
While basic shaders are incredibly useful, the true power of GLSL lies in its ability to perform complex mathematical calculations on the GPU. Because the GPU is designed to perform the same operation on many pixels simultaneously, shaders can execute these calculations incredibly fast, enabling real-time visual effects that would be impossible to achieve with the CPU alone.
One common advanced technique is procedural texturing. Instead of loading an image file, you can write a shader that generates a texture algorithmically. For example, you could use Perlin noise or sine waves to create procedural wood, marble, or fire textures. These textures have the advantage of being resolution-independent and can be animated over time simply by passing a time variable as a uniform to the shader.
Another powerful application of shaders is post-processing. A post-processing shader takes the final rendered image of your scene and applies effects to it, much like applying filters in a photo editing app. You can use post-processing shaders to add bloom, depth of field, color grading, or even simulate the look of an old CRT monitor or film camera.
When working with shaders, it's crucial to understand the graphics pipeline. The vertex shader always runs first, calculating the final position of each vertex on the screen. The output of the vertex shader is then interpolated and passed to the fragment shader, which runs for every pixel covered by the geometry. By manipulating the data as it flows through this pipeline, you have absolute control over the final image.
Optimizing Your 3D Scenes¶
As you add more lights, complex models, and high-resolution textures to your scenes, you may notice that the frame rate starts to drop. This is because every additional element requires more calculations from the computer. To ensure that your interactive sketches run smoothly, it's essential to practice good optimization techniques.
One of the most effective ways to optimize a 3D scene is to reduce the polygon count of your models. The 3D Mesh Vertex Array can contain thousands or even millions of points, and processing all of them can be computationally expensive. By simplifying your models in a program like Blender before exporting them, you can significantly improve performance without sacrificing too much visual quality.
Another important optimization strategy is to use lights sparingly. Calculating the interactions between multiple light sources and complex materials can quickly overwhelm the GPU. Try to achieve the desired lighting effect with as few lights as possible. For example, you might be able to replace several point lights with a single directional light or use a texture map to simulate complex lighting details.
Finally, be mindful of the shaders you use. While shaders are fast, a poorly written shader can still cause performance issues. Avoid using complex mathematical functions or long loops in your fragment shaders, as these will be executed for every single pixel on the screen. By profiling your sketches and identifying performance bottlenecks, you can ensure that your digital stage always runs at a smooth 60 frames per second.
Deep Dive: The Physics of Light¶
To truly master our digital stage, we must understand the physical properties of light that we are simulating. Real-world light is a form of electromagnetic radiation. When light from a source like the sun or a lightbulb hits an object, several things can happen. It can be absorbed, transmitted, or reflected.
In our 3D computer graphics models, we primarily care about reflection. There are two main types of reflection: diffuse and specular. Diffuse reflection happens when light hits a rough surface and scatters in all directions. This is what gives an object its base color and makes it visible regardless of the camera's position. In p5.js, this is simulated using ambient and directional lights combined with ambient and basic materials.
Specular reflection, on the other hand, happens when light hits a smooth, shiny surface and bounces off in a concentrated beam, much like a mirror. This creates the bright highlights we see on polished metal or wet objects. The Shininess Parameter in our specular materials controls the microscopic roughness of the surface, determining whether the specular highlight is sharp and small (smooth surface) or wide and soft (rougher surface).
Understanding these physical principles allows you, the director, to make informed decisions about how to light your scene to achieve the desired mood and realism. By carefully balancing the different types of light sources and materials, you can create scenes that range from cartoonish and flat to highly realistic and atmospheric.
Advanced Shader Techniques¶
While basic shaders are incredibly useful, the true power of GLSL lies in its ability to perform complex mathematical calculations on the GPU. Because the GPU is designed to perform the same operation on many pixels simultaneously, shaders can execute these calculations incredibly fast, enabling real-time visual effects that would be impossible to achieve with the CPU alone.
One common advanced technique is procedural texturing. Instead of loading an image file, you can write a shader that generates a texture algorithmically. For example, you could use Perlin noise or sine waves to create procedural wood, marble, or fire textures. These textures have the advantage of being resolution-independent and can be animated over time simply by passing a time variable as a uniform to the shader.
Another powerful application of shaders is post-processing. A post-processing shader takes the final rendered image of your scene and applies effects to it, much like applying filters in a photo editing app. You can use post-processing shaders to add bloom, depth of field, color grading, or even simulate the look of an old CRT monitor or film camera.
When working with shaders, it's crucial to understand the graphics pipeline. The vertex shader always runs first, calculating the final position of each vertex on the screen. The output of the vertex shader is then interpolated and passed to the fragment shader, which runs for every pixel covered by the geometry. By manipulating the data as it flows through this pipeline, you have absolute control over the final image.
Optimizing Your 3D Scenes¶
As you add more lights, complex models, and high-resolution textures to your scenes, you may notice that the frame rate starts to drop. This is because every additional element requires more calculations from the computer. To ensure that your interactive sketches run smoothly, it's essential to practice good optimization techniques.
One of the most effective ways to optimize a 3D scene is to reduce the polygon count of your models. The 3D Mesh Vertex Array can contain thousands or even millions of points, and processing all of them can be computationally expensive. By simplifying your models in a program like Blender before exporting them, you can significantly improve performance without sacrificing too much visual quality.
Another important optimization strategy is to use lights sparingly. Calculating the interactions between multiple light sources and complex materials can quickly overwhelm the GPU. Try to achieve the desired lighting effect with as few lights as possible. For example, you might be able to replace several point lights with a single directional light or use a texture map to simulate complex lighting details.
Finally, be mindful of the shaders you use. While shaders are fast, a poorly written shader can still cause performance issues. Avoid using complex mathematical functions or long loops in your fragment shaders, as these will be executed for every single pixel on the screen. By profiling your sketches and identifying performance bottlenecks, you can ensure that your digital stage always runs at a smooth 60 frames per second.
Deep Dive: The Physics of Light¶
To truly master our digital stage, we must understand the physical properties of light that we are simulating. Real-world light is a form of electromagnetic radiation. When light from a source like the sun or a lightbulb hits an object, several things can happen. It can be absorbed, transmitted, or reflected.
In our 3D computer graphics models, we primarily care about reflection. There are two main types of reflection: diffuse and specular. Diffuse reflection happens when light hits a rough surface and scatters in all directions. This is what gives an object its base color and makes it visible regardless of the camera's position. In p5.js, this is simulated using ambient and directional lights combined with ambient and basic materials.
Specular reflection, on the other hand, happens when light hits a smooth, shiny surface and bounces off in a concentrated beam, much like a mirror. This creates the bright highlights we see on polished metal or wet objects. The Shininess Parameter in our specular materials controls the microscopic roughness of the surface, determining whether the specular highlight is sharp and small (smooth surface) or wide and soft (rougher surface).
Understanding these physical principles allows you, the director, to make informed decisions about how to light your scene to achieve the desired mood and realism. By carefully balancing the different types of light sources and materials, you can create scenes that range from cartoonish and flat to highly realistic and atmospheric.
Advanced Shader Techniques¶
While basic shaders are incredibly useful, the true power of GLSL lies in its ability to perform complex mathematical calculations on the GPU. Because the GPU is designed to perform the same operation on many pixels simultaneously, shaders can execute these calculations incredibly fast, enabling real-time visual effects that would be impossible to achieve with the CPU alone.
One common advanced technique is procedural texturing. Instead of loading an image file, you can write a shader that generates a texture algorithmically. For example, you could use Perlin noise or sine waves to create procedural wood, marble, or fire textures. These textures have the advantage of being resolution-independent and can be animated over time simply by passing a time variable as a uniform to the shader.
Another powerful application of shaders is post-processing. A post-processing shader takes the final rendered image of your scene and applies effects to it, much like applying filters in a photo editing app. You can use post-processing shaders to add bloom, depth of field, color grading, or even simulate the look of an old CRT monitor or film camera.
When working with shaders, it's crucial to understand the graphics pipeline. The vertex shader always runs first, calculating the final position of each vertex on the screen. The output of the vertex shader is then interpolated and passed to the fragment shader, which runs for every pixel covered by the geometry. By manipulating the data as it flows through this pipeline, you have absolute control over the final image.
Optimizing Your 3D Scenes¶
As you add more lights, complex models, and high-resolution textures to your scenes, you may notice that the frame rate starts to drop. This is because every additional element requires more calculations from the computer. To ensure that your interactive sketches run smoothly, it's essential to practice good optimization techniques.
One of the most effective ways to optimize a 3D scene is to reduce the polygon count of your models. The 3D Mesh Vertex Array can contain thousands or even millions of points, and processing all of them can be computationally expensive. By simplifying your models in a program like Blender before exporting them, you can significantly improve performance without sacrificing too much visual quality.
Another important optimization strategy is to use lights sparingly. Calculating the interactions between multiple light sources and complex materials can quickly overwhelm the GPU. Try to achieve the desired lighting effect with as few lights as possible. For example, you might be able to replace several point lights with a single directional light or use a texture map to simulate complex lighting details.
Finally, be mindful of the shaders you use. While shaders are fast, a poorly written shader can still cause performance issues. Avoid using complex mathematical functions or long loops in your fragment shaders, as these will be executed for every single pixel on the screen. By profiling your sketches and identifying performance bottlenecks, you can ensure that your digital stage always runs at a smooth 60 frames per second.