https://codepen.io/im_paul_hi/pen/bObYOy?editors=0010
我试图编写一个基本的漫射光照着色器,但是当我在对象上应用任何类型的位置/旋转更改时,阴影似乎都没有得到更新。如果我移动“自定义点光源”位置,则似乎工作正常(多维数据集上的阴影正确更新),但是如果多维数据集本身在移动,则阴影看起来不正确。
如果取消注释第183和184行,则会应用旋转,并且阴影不正确。
cube.rotation.x += rotSpeed.x;
cube.rotation.y += rotSpeed.y;
class PromisedLoad {
static GenericLoader(loader, url, callback) {
return new Promise((resolve, reject) => {
loader.load(url, (object) => {
if (callback) {
callback(object, resolve);
} else {
resolve(object);
}
}, (progress) => {
console.log(progress);
}, (error) => {
reject(error);
});
});
}
static GetGLTF(url, callback) {
let gltfLoader = new THREE.GLTFLoader();
return this.GenericLoader(gltfLoader, url, callback);
}
}
let vertexShader2 = `
uniform float time;
uniform vec3 materialColor;
uniform vec3 ambientLightColor;
uniform float ambientLightStrength;
uniform vec3 customPointLightPos;
varying vec3 vNormal;
varying vec3 lightVec;
void main() {
// normal is an attribute passed in by threejs
vNormal = normal;
lightVec = normalize(customPointLightPos - position);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`;
// import customFragmentShader from './shaders/fragmentShader1.glsl';
let fragmentShader1 = `
uniform float time;
uniform vec3 materialColor;
uniform vec3 ambientLightColor;
uniform float ambientLightStrength;
uniform vec3 customPointLightPos;
varying vec3 vNormal;
varying vec3 lightVec;
void main() {
float dProd = max(0.0, dot(vNormal, lightVec));
vec3 c = mix(materialColor * dProd, ambientLightColor, ambientLightStrength);
gl_FragColor = vec4(c, 1.0);
}
`;
let mouse = new THREE.Vector2();
window.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('DOMContentLoaded', () => {
let renderer,
camera,
scene = null;
const container = document.getElementById('container');
let controls;
let startTime, time;
let cube;
let rotSpeed = new THREE.Vector3(0.05, 0.03, 0.0);
let axesHelper;
let uniforms;
let customPointLight;
initialize();
// console.log('rotSpeed: ', rotSpeed);
// setupGUI();
async function initialize() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
antialias: true // to get smoother output
});
renderer.setClearColor(0x3b3b3b);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
// create a camera in the scene
camera = new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
1,
10000
);
axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
addCube();
addCustomPointLight();
controls = new THREE.OrbitControls(camera);
scene.add(camera);
camera.position.z = 5;
controls.update();
// and then just look at it!
camera.lookAt(scene.position);
controls.update();
animate();
}
function addCube() {
// let geometry = new THREE.SphereGeometry(1, 32, 32);
let geometry = new THREE.BoxGeometry(1,1,1);
uniforms = {
time: {
type: 'f',
value: 0
},
materialColor: {
type: 'v3f',
value: new THREE.Vector3(1.0, 0.0, 0.0)
},
ambientLightColor: {
type: 'v3f',
value: new THREE.Vector3(0.0, 0.0, 1.0)
},
ambientLightStrength: {
type: 'f',
value: 0.3
},
customPointLightPos: {
type: 'v3f',
value: new THREE.Vector3(2.0, 2.0, 2.0)
}
};
const shaderMaterialParams = {
uniforms: uniforms,
vertexShader: vertexShader2,
fragmentShader: fragmentShader1
};
const customMaterial = new THREE.ShaderMaterial(shaderMaterialParams);
cube = new THREE.Mesh(geometry, customMaterial);
scene.add(cube);
}
function addCustomPointLight() {
let geo = new THREE.BoxGeometry(0.3, 0.3, 0.3);
let mat = new THREE.MeshBasicMaterial();
customPointLight = new THREE.Mesh(geo, mat);
customPointLight.position.set(2, 2, 2);
scene.add(customPointLight);
}
function normalize(x, fromMin, fromMax) {
let totalRange;
x = Math.abs(x);
totalRange = Math.abs(fromMin) + Math.abs(fromMax);
// now we can map out the range from 0 to the totalRange and get a normalized (0 - 1) value
return x / totalRange;
}
function animate() {
requestAnimationFrame(animate);
time = performance.now() / 1000;
cube.material.uniforms.time.value = time;
cube.rotation.x += rotSpeed.x;
cube.rotation.y += rotSpeed.y;
render();
}
function render() {
renderer.render(scene, camera);
controls.update();
}
setupGUI(rotSpeed, uniforms, cube, customPointLight);
});
function setupGUI(rotSpeed, uniforms, cube, customPointLight) {
let options = {
velx: 0,
vely: 0,
rotSpeed: rotSpeed,
materialColor: uniforms.materialColor.value.toArray(),
ambientLightColor: uniforms.ambientLightColor.value.toArray(),
ambientLightStrength: uniforms.ambientLightStrength.value,
customPointLightPos: {
x: 2,
y: 2,
z: 2
}
};
let gui = new dat.GUI();
let rotation = gui.addFolder('Rotation');
rotation
.add(options.rotSpeed, 'x', -0.02, 0.02)
.name('X')
.listen();
rotation
.add(options.rotSpeed, 'y', -0.02, 0.02)
.name('Y')
.listen();
rotation.open();
let uniformsGUI = gui.addFolder('Uniforms');
uniformsGUI
.addColor(options, 'materialColor')
.onChange(function(value) {
cube.material.uniforms.materialColor.value.x = value[0] / 255;
cube.material.uniforms.materialColor.value.y = value[1] / 255;
cube.material.uniforms.materialColor.value.z = value[2] / 255;
})
.name('materialColor')
.listen();
uniformsGUI.addColor(options, 'ambientLightColor').onChange(function(value) {
cube.material.uniforms.ambientLightColor.value.x = value[0] / 255;
cube.material.uniforms.ambientLightColor.value.y = value[1] / 255;
cube.material.uniforms.ambientLightColor.value.z = value[2] / 255;
});
uniformsGUI
.add(options, 'ambientLightStrength', 0.0, 1.0)
.onChange(function(value) {
cube.material.uniforms.ambientLightStrength.value = value;
});
uniformsGUI.open();
let customPointLightGUI = gui.addFolder('Custom Point Light');
customPointLightGUI
.add(customPointLight.position, 'x', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.x = value;
});
customPointLightGUI
.add(customPointLight.position, 'y', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.y = value;
});
customPointLightGUI
.add(customPointLight.position, 'z', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.z = value;
});
customPointLightGUI.open();
let box = gui.addFolder('Cube');
box
.add(cube.scale, 'x', 0, 3)
.name('Width')
.listen();
box
.add(cube.scale, 'y', 0, 3)
.name('Height')
.listen();
box
.add(cube.scale, 'z', 0, 3)
.name('Length')
.listen();
box.add(cube.material, 'wireframe').listen();
box.open();
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
html, body {
height: 100%;
width: 100%;
font-size: 100%;
font-family: 'Roboto', sans-serif;
text-align: center;
font-weight: lighter;
background: grey;
overflow-y: hidden;
}
a{
color: inherit;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js"></script>
<script src="https://unpkg.com/three@0.97/examples/js/loaders/GLTFLoader.js"></script>
<script src="https://unpkg.com/three@0.97/examples/js/controls/OrbitControls.js"></script>
<div id="container"></div>
<!--https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.js-->
答案 0 :(得分:2)
引起此问题的原因是,在片段着色器中,vNormal
是模型空间中的向量,position
是模型空间中的点,而customPointLightPos
是世界空间中的位置。 / p>
您必须从模型空间转换vNormal
才能在顶点着色器中查看空间。这可以由THREE.js提供的normalMatrix
完成。
要计算光矢量,必须将position
从模型空间转换为视图空间,这可以通过modelViewMatrix
完成。
并且您必须将customPointLightPos
从世界空间转换为视图空间,这可以通过viewMatrix
完成:
vNormal = normalMatrix * normal;
vec4 viewPos = modelViewMatrix * vec4(position, 1.0);
vec4 viewLightPos = viewMatrix * vec4(customPointLightPos, 1.0);
lightVec = normalize(viewLightPos.xyz - viewPos.xyz);
这导致两个向量都与同一个参考系统相关,并且可以分别进行比较以用于光计算。
请参阅eaxmaple,然后将建议的更改应用于原始代码:
class PromisedLoad {
static GenericLoader(loader, url, callback) {
return new Promise((resolve, reject) => {
loader.load(url, (object) => {
if (callback) {
callback(object, resolve);
} else {
resolve(object);
}
}, (progress) => {
console.log(progress);
}, (error) => {
reject(error);
});
});
}
static GetGLTF(url, callback) {
let gltfLoader = new THREE.GLTFLoader();
return this.GenericLoader(gltfLoader, url, callback);
}
}
let vertexShader2 = `
uniform float time;
uniform vec3 materialColor;
uniform vec3 ambientLightColor;
uniform float ambientLightStrength;
uniform vec3 customPointLightPos;
varying vec3 vNormal;
varying vec3 lightVec;
void main() {
vNormal = normalMatrix * normal;
vec4 viewPos = modelViewMatrix * vec4(position, 1.0);
vec4 viewLightPos = viewMatrix * vec4(customPointLightPos, 1.0);
lightVec = normalize(viewLightPos.xyz - viewPos.xyz);
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
`;
// import customFragmentShader from './shaders/fragmentShader1.glsl';
let fragmentShader1 = `
uniform float time;
uniform vec3 materialColor;
uniform vec3 ambientLightColor;
uniform float ambientLightStrength;
uniform vec3 customPointLightPos;
varying vec3 vNormal;
varying vec3 lightVec;
void main() {
float dProd = max(0.0, dot(vNormal, lightVec));
vec3 c = mix(materialColor * dProd, ambientLightColor, ambientLightStrength);
gl_FragColor = vec4(c, 1.0);
}
`;
let mouse = new THREE.Vector2();
window.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener('DOMContentLoaded', () => {
let renderer,
camera,
scene = null;
const container = document.getElementById('container');
let controls;
let startTime, time;
let cube;
let rotSpeed = new THREE.Vector3(0.05, 0.03, 0.0);
let axesHelper;
let uniforms;
let customPointLight;
initialize();
// console.log('rotSpeed: ', rotSpeed);
// setupGUI();
async function initialize() {
scene = new THREE.Scene();
renderer = new THREE.WebGLRenderer({
antialias: true // to get smoother output
});
renderer.setClearColor(0x3b3b3b);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
// create a camera in the scene
camera = new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
1,
10000
);
axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
addCube();
addCustomPointLight();
controls = new THREE.OrbitControls(camera);
scene.add(camera);
camera.position.z = 10;
controls.update();
// and then just look at it!
camera.lookAt(scene.position);
controls.update();
window.onresize = resize;
animate();
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function addCube() {
// let geometry = new THREE.SphereGeometry(1, 32, 32);
let geometry = new THREE.BoxGeometry(1,1,1);
uniforms = {
time: {
type: 'f',
value: 0
},
materialColor: {
type: 'v3f',
value: new THREE.Vector3(1.0, 0.0, 0.0)
},
ambientLightColor: {
type: 'v3f',
value: new THREE.Vector3(0.0, 0.0, 1.0)
},
ambientLightStrength: {
type: 'f',
value: 0.3
},
customPointLightPos: {
type: 'v3f',
value: new THREE.Vector3(2.0, 2.0, 2.0)
}
};
const shaderMaterialParams = {
uniforms: uniforms,
vertexShader: vertexShader2,
fragmentShader: fragmentShader1
};
const customMaterial = new THREE.ShaderMaterial(shaderMaterialParams);
cube = new THREE.Mesh(geometry, customMaterial);
scene.add(cube);
}
function addCustomPointLight() {
let geo = new THREE.BoxGeometry(0.3, 0.3, 0.3);
let mat = new THREE.MeshBasicMaterial();
customPointLight = new THREE.Mesh(geo, mat);
customPointLight.position.set(2, 2, 2);
scene.add(customPointLight);
}
function normalize(x, fromMin, fromMax) {
let totalRange;
x = Math.abs(x);
totalRange = Math.abs(fromMin) + Math.abs(fromMax);
// now we can map out the range from 0 to the totalRange and get a normalized (0 - 1) value
return x / totalRange;
}
function animate() {
requestAnimationFrame(animate);
time = performance.now() / 1000;
cube.material.uniforms.time.value = time;
cube.rotation.x += rotSpeed.x;
cube.rotation.y += rotSpeed.y;
render();
}
function render() {
renderer.render(scene, camera);
controls.update();
}
setupGUI(rotSpeed, uniforms, cube, customPointLight);
});
function setupGUI(rotSpeed, uniforms, cube, customPointLight) {
let options = {
velx: 0,
vely: 0,
rotSpeed: rotSpeed,
materialColor: uniforms.materialColor.value.toArray(),
ambientLightColor: uniforms.ambientLightColor.value.toArray(),
ambientLightStrength: uniforms.ambientLightStrength.value,
customPointLightPos: {
x: 2,
y: 2,
z: 2
}
};
let gui = new dat.GUI();
let rotation = gui.addFolder('Rotation');
rotation
.add(options.rotSpeed, 'x', -0.02, 0.02)
.name('X')
.listen();
rotation
.add(options.rotSpeed, 'y', -0.02, 0.02)
.name('Y')
.listen();
rotation.open();
let uniformsGUI = gui.addFolder('Uniforms');
uniformsGUI
.addColor(options, 'materialColor')
.onChange(function(value) {
cube.material.uniforms.materialColor.value.x = value[0] / 255;
cube.material.uniforms.materialColor.value.y = value[1] / 255;
cube.material.uniforms.materialColor.value.z = value[2] / 255;
})
.name('materialColor')
.listen();
uniformsGUI.addColor(options, 'ambientLightColor').onChange(function(value) {
cube.material.uniforms.ambientLightColor.value.x = value[0] / 255;
cube.material.uniforms.ambientLightColor.value.y = value[1] / 255;
cube.material.uniforms.ambientLightColor.value.z = value[2] / 255;
});
uniformsGUI
.add(options, 'ambientLightStrength', 0.0, 1.0)
.onChange(function(value) {
cube.material.uniforms.ambientLightStrength.value = value;
});
uniformsGUI.open();
let customPointLightGUI = gui.addFolder('Custom Point Light');
customPointLightGUI
.add(customPointLight.position, 'x', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.x = value;
});
customPointLightGUI
.add(customPointLight.position, 'y', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.y = value;
});
customPointLightGUI
.add(customPointLight.position, 'z', -5, 5)
.onChange(function(value) {
cube.material.uniforms.customPointLightPos.value.z = value;
});
customPointLightGUI.open();
let box = gui.addFolder('Cube');
box
.add(cube.scale, 'x', 0, 3)
.name('Width')
.listen();
box
.add(cube.scale, 'y', 0, 3)
.name('Height')
.listen();
box
.add(cube.scale, 'z', 0, 3)
.name('Length')
.listen();
box.add(cube.material, 'wireframe').listen();
box.open();
}
function onDocumentMouseMove(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
html, body {
margin: 0;
height: 100%;
width: 100%;
font-size: 100%;
font-family: 'Roboto', sans-serif;
text-align: center;
font-weight: lighter;
background: grey;
overflow-y: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/99/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/loaders/GLTFLoader.js"></script>
<div id="container"></div>