英文:
How do I keep the color blending the same when orbiting a threejs object?
问题
目前,当我围绕我所绘制的矩形旋转时,颜色会改变,因为它们基于法线,如何保持颜色不变?我正在使用orbitcontrols库附加程序。
以下是JavaScript代码的翻译:
/* import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js'; */
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
let camera, scene, renderer, controls;
init();
render();
function init() {
let width = $('#container').width();
let height = $('#container').height();
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width, height);
$("#container").html(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, -1, 0,
2, -1, 0,
2, 1, 0,
-2, 1, 0
];
const indices = [
0, 1, 2, // 第一个三角形
2, 3, 0 // 第二个三角形
];
const normals = [
1, 0, 0, // 左下
0, 0, 1, // 右下
0, 0, 1, // 右上
1, 0, 0 // 左上
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
const material = new THREE.MeshNormalMaterial({ vertexColors: true, flatShading: false });
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.update();
animate();
}
function animate() {
requestAnimationFrame(animate);
controls.update(); // 只有在controls.enableDamping = true或controls.autoRotate = true时才需要
render();
}
function render() {
renderer.render(scene, camera);
}
在设置法线属性以在矩形的面上显示多种颜色时,它们会根据相机围绕矩形的旋转而改变。我原本希望颜色保持不变,而不受旋转的影响。
我在网上搜索,但找不到任何好的示例来解释这个问题以及如何解决它。
我怀疑相机移动时法线被操纵,因此我可能需要以某种方式更新法线,以便根据旋转保持正确的颜色,但我不知道如何实现这一点。
英文:
Currently when I rotate around the rectangle that I have drawn, the colors change because they're based on the normals, how can I keep the colors the same? I'm using the orbitcontrols library addon.
Attached below is the jsfiddle and the code:
/* import * as THREE from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/build/three.module.js'; */
import {OrbitControls} from 'https://threejsfundamentals.org/threejs/resources/threejs/r110/examples/jsm/controls/OrbitControls.js';
let camera, scene, renderer, controls;
init();
render();
function init() {
let width = $('#container').width();
let height = $('#container').height();
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(width,height);
$("#container").html(renderer.domElement);
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
camera.position.z = 4;
scene = new THREE.Scene();
const geometry = new THREE.BufferGeometry();
const vertices = [
-2, -1, 0,
2, -1, 0,
2, 1, 0,
-2, 1, 0
];
const indices = [
0, 1, 2, // first triangle
2, 3, 0 // second triangle
];
const normals = [
1,0,0, // bottom left
0,0,1, // bottom right
0,0,1, // top right
1,0,0 // top left
];
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
const material = new THREE.MeshNormalMaterial({vertexColors:true,flatShading:false});
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
controls = new OrbitControls( camera, renderer.domElement );
controls.target.set(0, 0, 0);
controls.update();
animate();
}
function animate() {
requestAnimationFrame( animate );
controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true
render();
}
function render() {
renderer.render(scene, camera);
}
https://jsfiddle.net/ZQVerse/2an9edhL/
When I set the normals attribute to try to get more than one color showing on the faces of the rectangle, they change based on how the camera is orbited around the rectangle. I was expecting the colors to be the same, regardless of rotation.
I looked online but I couldn't find any good examples of what would be causing this issue and how to fix it.
I suspect the normals are getting manipulated when the camera moves, so I probably have to update the normals somehow to keep the colors correct based on the rotation, but I don't know how to accomplish this.
答案1
得分: 2
正常的材质始终会根据法线和相机之间的角度而改变。这是默认行为,因为法线主要用于光照。您希望查看表面是否反射光线到相机,因此了解表面的法线是否直接面向相机非常重要(这很简单,可能不完全准确)。
这里有一个使用世界空间而不是视图空间的新法线材质(法线位置和相机位置组合)的存储库:https://github.com/maximeq/three-js-mesh-world-normal-material
以及更新后的jsfiddle,其中使用自定义着色器,不将视图应用于法线。
https://jsfiddle.net/szamkh5w/1/
const material = new THREE.ShaderMaterial( {
vertexShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );",
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}",
].join( "\n" ),
fragmentShader: [
"varying vec3 vNormalW;",
"void main() {",
" gl_FragColor = vec4( vNormalW, 1.);",
"}",
].join( "\n" )
} );
希望这能帮助您。
英文:
The normal material always changes depending of the angle between the normal and the camera. Which is the default behavior because normals are mostly used for lightning. And you want to see if the surface is reflecting the light to the camara, so it's important to know if the normal of the surface is directly facing to the camera (very simple and may not be completely true)
here is a repo with a new normal material which uses the world space and not the view space (Normal Position and Camera Position combined): https://github.com/maximeq/three-js-mesh-world-normal-material
and the updated jsfiddle with a custom shader, which doesn't apply the view to the normal.
https://jsfiddle.net/szamkh5w/1/
const material = new THREE.ShaderMaterial( {
vertexShader: [
"varying vec3 vPositionW;",
"varying vec3 vNormalW;",
"void main() {",
" vNormalW = normalize( vec3( vec4( normal, 0.0 ) * modelMatrix ) );",
" gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}" ].join( "\n" ),
fragmentShader: [
"varying vec3 vNormalW;",
"void main() {",
" gl_FragColor = vec4( vNormalW, 1.);",
"}"
].join( "\n" )
} );
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论