mirror of
https://github.com/visgl/luma.gl.git
synced 2026-01-18 14:03:42 +00:00
261 lines
6.7 KiB
Plaintext
261 lines
6.7 KiB
Plaintext
import {DeviceTabs} from '@site/src/react-luma';
|
|
import {LightingExample} from '@site';
|
|
|
|
# Lighting
|
|
|
|
This tutorial adds some lighting to enhance the feeling of 3D in the scene.
|
|
|
|
:::caution
|
|
The tutorial pages have not yet been updated for luma.gl v9.
|
|
:::
|
|
|
|
<DeviceTabs />
|
|
<LightingExample />
|
|
|
|
To add lighting, we'll use one of built-in luma.gl shader modules for the first time.
|
|
To start, we'll add the `phongLighting` module from **@luma.gl/shadertools** to our imports:
|
|
|
|
```typescript
|
|
import {AnimationLoop, Model, CubeGeometry} from '@luma.gl/engine';
|
|
import {Texture2D, clear, setParameters} from '@luma.gl/webgl';
|
|
import {phongLighting} from '@luma.gl/shadertools';
|
|
import {Matrix4} from '@math.gl/core';
|
|
```
|
|
|
|
The `phongLighting` shader module adds functions to our fragment shader to facilitate lighting calculations.
|
|
|
|
We'll modify our shaders to perform our lighting calculations in the following ways:
|
|
|
|
- We'll input the surface `normals` as an in.
|
|
- We'll pass the world positions and normals to the fragment shader.
|
|
- We'll call `lighting_getLightColor`, which will be added to our fragment shader by the `phongLighting` module, to calculate the final fragment color.
|
|
|
|
```typescript
|
|
const vs = `\
|
|
in vec3 positions;
|
|
in vec3 normals;
|
|
in vec2 texCoords;
|
|
|
|
uniform mat4 uModel;
|
|
uniform mat4 uMVP;
|
|
|
|
out vec3 vPosition;
|
|
out vec3 vNormal;
|
|
out vec2 vUV;
|
|
|
|
void main(void) {
|
|
vPosition = (uModel * vec4(positions, 1.0)).xyz;
|
|
vNormal = mat3(uModel) * normals;
|
|
vUV = texCoords;
|
|
gl_Position = uMVP * vec4(positions, 1.0);
|
|
}
|
|
`;
|
|
|
|
const fs = `\
|
|
precision highp float;
|
|
|
|
uniform sampler2D uTexture;
|
|
uniform vec3 uEyePosition;
|
|
|
|
in vec3 vPosition;
|
|
in vec3 vNormal;
|
|
in vec2 vUV;
|
|
|
|
void main(void) {
|
|
vec3 materialColor = texture2D(uTexture, vec2(vUV.x, 1.0 - vUV.y)).rgb;
|
|
vec3 surfaceColor = lighting_getLightColor(materialColor, uEyePosition, vPosition, normalize(vNormal));
|
|
|
|
gl_FragColor = vec4(surfaceColor, 1.0);
|
|
}
|
|
`;
|
|
```
|
|
|
|
Our `onInitialize` method needs a few significant updates:
|
|
|
|
```typescript
|
|
override onInitialize({device}) {
|
|
setParameters(device, {
|
|
depthTest: true,
|
|
depthFunc: gl.LEQUAL
|
|
});
|
|
|
|
const texture = device.createTexture({data: 'vis-logo.png'});
|
|
|
|
const eyePosition = [0, 0, 5];
|
|
const modelMatrix = new Matrix4();
|
|
const viewMatrix = new Matrix4().lookAt({eye: eyePosition});
|
|
const mvpMatrix = new Matrix4();
|
|
|
|
const model = new Model(device, {
|
|
vs,
|
|
fs,
|
|
geometry: new CubeGeometry(),
|
|
uniforms: {
|
|
uTexture: texture,
|
|
uEyePosition: eyePosition
|
|
},
|
|
modules: [phongLighting],
|
|
moduleSettings: {
|
|
material: {
|
|
specularColor: [255, 255, 255]
|
|
},
|
|
lights: [
|
|
{
|
|
type: 'ambient',
|
|
color: [255, 255, 255]
|
|
},
|
|
{
|
|
type: 'point',
|
|
color: [255, 255, 255],
|
|
position: [1, 2, 1]
|
|
}
|
|
]
|
|
}
|
|
});
|
|
|
|
return {
|
|
model,
|
|
modelMatrix,
|
|
viewMatrix,
|
|
mvpMatrix
|
|
};
|
|
}
|
|
```
|
|
|
|
We're splitting the model matrix out on its own so we can use it in our shaders to transform the positions and normals for the lighting calculations. The biggest change, however, is the `moduleSettings` parameter we're passing to our `Model` constructor. `moduleSettings` are passed on to shader modules to help them set up uniforms. In this case, we're passing some material and light properties that `phongLighting` uses to perform its lighting calculations in `lighting_getLightColor`.
|
|
|
|
Our `onRender` doesn't change much except to set up the model matrix separately from the MVP matrix and pass it as a uniform:
|
|
|
|
```typescript
|
|
override onRender({device, aspect, tick, model, mvpMatrix, viewMatrix}) {
|
|
modelMatrix
|
|
.identity()
|
|
.rotateX(tick * 0.01)
|
|
.rotateY(tick * 0.013);
|
|
|
|
mvpMatrix
|
|
.perspective({fovy: Math.PI / 3, aspect})
|
|
.multiplyRight(viewMatrix)
|
|
.multiplyRight(modelMatrix);
|
|
|
|
clear(device, {color: [0, 0, 0, 1], depth: true});
|
|
|
|
model.setUniforms({uMVP: mvpMatrix, uModel: modelMatrix}).draw();
|
|
}
|
|
```
|
|
|
|
If all went well, you should see a scene almost identical to the one from the previous "hello cube" tutorial but with some white light reflecting off the cube.
|
|
|
|
```typescript
|
|
import {AnimationLoop, Model, CubeGeometry} from '@luma.gl/engine';
|
|
import {Texture2D, clear, setParameters} from '@luma.gl/webgl';
|
|
import {phongLighting} from '@luma.gl/shadertools';
|
|
import {Matrix4} from '@math.gl/core';
|
|
|
|
const vs = `\
|
|
in vec3 positions;
|
|
in vec3 normals;
|
|
in vec2 texCoords;
|
|
|
|
uniform mat4 uModel;
|
|
uniform mat4 uMVP;
|
|
|
|
out vec3 vPosition;
|
|
out vec3 vNormal;
|
|
out vec2 vUV;
|
|
|
|
void main(void) {
|
|
vPosition = (uModel * vec4(positions, 1.0)).xyz;
|
|
vNormal = mat3(uModel) * normals;
|
|
vUV = texCoords;
|
|
gl_Position = uMVP * vec4(positions, 1.0);
|
|
}
|
|
`;
|
|
|
|
const fs = `\
|
|
precision highp float;
|
|
|
|
uniform sampler2D uTexture;
|
|
uniform vec3 uEyePosition;
|
|
|
|
in vec3 vPosition;
|
|
in vec3 vNormal;
|
|
in vec2 vUV;
|
|
|
|
void main(void) {
|
|
vec3 materialColor = texture2D(uTexture, vec2(vUV.x, 1.0 - vUV.y)).rgb;
|
|
vec3 surfaceColor = lighting_getLightColor(materialColor, uEyePosition, vPosition, normalize(vNormal));
|
|
|
|
gl_FragColor = vec4(surfaceColor, 1.0);
|
|
}
|
|
`;
|
|
|
|
const loop = new AnimationLoop({
|
|
override onInitialize({device}) {
|
|
setParameters(device, {
|
|
depthTest: true,
|
|
depthFunc: gl.LEQUAL
|
|
});
|
|
|
|
const texture = device.createTexture({data: 'vis-logo.png'});
|
|
|
|
const eyePosition = [0, 0, 5];
|
|
const modelMatrix = new Matrix4();
|
|
const viewMatrix = new Matrix4().lookAt({eye: eyePosition});
|
|
const mvpMatrix = new Matrix4();
|
|
|
|
const model = new Model(device, {
|
|
vs,
|
|
fs,
|
|
geometry: new CubeGeometry(),
|
|
uniforms: {
|
|
uTexture: texture,
|
|
uEyePosition: eyePosition
|
|
},
|
|
modules: [phongLighting],
|
|
moduleSettings: {
|
|
material: {
|
|
specularColor: [255, 255, 255]
|
|
},
|
|
lights: [
|
|
{
|
|
type: 'ambient',
|
|
color: [255, 255, 255]
|
|
},
|
|
{
|
|
type: 'point',
|
|
color: [255, 255, 255],
|
|
position: [1, 2, 1]
|
|
}
|
|
]
|
|
}
|
|
});
|
|
|
|
return {
|
|
model,
|
|
modelMatrix,
|
|
viewMatrix,
|
|
mvpMatrix
|
|
};
|
|
},
|
|
|
|
override onRender({device, aspect, tick, model, mvpMatrix, viewMatrix}) {
|
|
modelMatrix
|
|
.identity()
|
|
.rotateX(tick * 0.01)
|
|
.rotateY(tick * 0.013);
|
|
|
|
mvpMatrix
|
|
.perspective({fovy: Math.PI / 3, aspect})
|
|
.multiplyRight(viewMatrix)
|
|
.multiplyRight(modelMatrix);
|
|
|
|
clear(device, {color: [0, 0, 0, 1], depth: true});
|
|
|
|
model.setUniforms({uMVP: mvpMatrix, uModel: modelMatrix}).draw();
|
|
}
|
|
});
|
|
|
|
loop.start();
|
|
```
|