如何在绕三js对象轨道运动时保持颜色混合相同?

huangapple go评论67阅读模式
英文:

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" )
} );

huangapple
  • 本文由 发表于 2023年4月4日 12:04:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/75925433.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定