Three.js小白的学习之路。
之前博客写过一篇使用WebGLRenderTarget离屏渲染实现不同场景之间的过渡。本篇则主要是介绍一下WebGLRenderTarget以及一些其他使用,本篇参考了Three.js的官方中有关渲染目标的讲解。
渲染目标大体上指的是可以被渲染的纹理。当它被渲染之后,你可以像使用其他纹理一样使用它。意思就是可以将渲染目标输出的纹理当做一张图片纹理去使用(浅薄理解)。
官方示例
下面贴一下官网的一个例子:
const renderTargetWidth = 512;
const renderTargetHeight = 512;
const scene = new Three.Scene();
const camera = new Three.PerspectiveCamera(
75,
renderTargetWidth / renderTargetHeight,
0.1,
10
);
camera.position.set(0, 0, 2);
camera.lookAt(0, 0, 0);
const rtScene = new Three.Scene();
rtScene.background = new Three.Color("red");
const rtCamera = new Three.PerspectiveCamera(
75,
renderTargetWidth / renderTargetHeight,
0.1,
10
);
rtCamera.position.set(0, 0, 2);
rtCamera.lookAt(0, 0, 0);
const renderTarget = new Three.WebGLRenderTarget(
renderTargetWidth,
renderTargetHeight
);
const ambientLight = new Three.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
{
const light = new Three.DirectionalLight(0xffffff, 1);
light.position.set(-1, 2, 4);
rtScene.add(light);
}
const geometry = new Three.BoxGeometry(1, 1, 1);
const makeInstance = (geo: Three.BufferGeometry, color: number, x: number) => {
const material = new Three.MeshPhongMaterial({ color });
const cube = new Three.Mesh(geo, material);
rtScene.add(cube);
cube.position.x = x;
return cube;
};
const rtCube = [
makeInstance(geometry, 0x44aa88, 0),
makeInstance(geometry, 0x8844aa, -2),
makeInstance(geometry, 0xaa88aa, 2),
];
const material = new Three.MeshPhongMaterial({
map: renderTarget.texture,
});
const cube = new Three.Mesh(geometry, material);
scene.add(cube);
const loop = (time: number) => {
time *= 0.0001;
rtCube.forEach((cube, ndx) => {
const speed = 1 + ndx * 0.1;
const rot = time * speed;
cube.rotation.x = rot;
cube.rotation.y = rot;
});
renderer.setRenderTarget(renderTarget);
renderer.render(rtScene, rtCamera);
renderer.setRenderTarget(null);
renderer.render(scene, camera);
requestAnimationFrame((time) => {
loop(time);
});
};
loop(0);
代码很简单,不难理解。先看一下运行结果:
可以看到,图中最外层的立方体的纹理是一个由三个旋转立方体+红色背景组成的纹理。
怎么实现的呢?这就是WebGLRenderTarget的妙用之处了。
首先创建了一个renderTargetScene(rtScene)和renderTargetCamera(rtCamera),作为渲染目标所需的场景和摄像机,在生成了三个cube加入到rtScene中。
在loop循环中,并没有直接讲场景渲染到canvas中,而是加了中间一层WebGLRenderTarget。可以理解为一个以前都是直接拿钞票付款,没有中间商,现在变成了要付款,得先掏出手机,因为钱都在手机里,然后扫码付款。
手机就是WebGLRenderTarget,他将目标换一种形式保存了下来,在这里面就可以简单理解为一个图像纹理。
既然是图像,那么自然可将其作为模型的纹理贴图进行使用,因此最外层的盒子的map属性就可设置为renderTarget.texture,从而实现上述效果。
使用场景
一个就是之前提到过的离屏渲染。
一个是后处理效果,如在使用辉光等后处理时,经常会导致场景变的模糊,加入一个WebGLRenderTarget可以明显改善,官方在实现相关的后处理特效时也使用了WebGLRenderTarget。
此外还可以作为3D场景中可能会用到的镜子反射(像龙骑里面一个人被困在镜子里面,只有一半身体可以看到那种)、监控画面等需要看到不是当前所能看到的场景等。
下面写一个简单的例子,车子在行驶过程中,后视镜突然变成一个别的东西(常见的鬼片里面会出现的那种,但我做的很简单很粗糙,凑活看一看):
const renderTargetWidth = 512;
const renderTargetHeight = 512;
const scene = new Three.Scene();
const camera = new Three.PerspectiveCamera(
60,
window.innerWidth / window.innerHeight,
0.1,
10
);
camera.position.set(-2.5, 1.5, -2.5);
camera.lookAt(0, 0, 0);
const rtScene = new Three.Scene();
rtScene.background = new Three.Color(0xff0000);
const rtCamera = new Three.PerspectiveCamera(
60,
renderTargetWidth / renderTargetHeight,
0.1,
10
);
rtCamera.position.set(-2.5, 1.5, -2.5);
rtCamera.lookAt(0, 0, 0);
const geo = new Three.TorusGeometry(0.5, 0.3);
const material = new Three.MeshBasicMaterial({ color: 0x00ff00 });
const torus = new Three.Mesh(geo, material);
rtScene.add(torus);
const renderTarget = new Three.WebGLRenderTarget(
renderTargetWidth,
renderTargetHeight
);
const wheel: Three.Mesh[] = [];
let mirror: Three.Mesh;
new GLTFLoader().load("/car.glb", (glb) => {
const mesh = glb.scene;
scene.add(mesh);
mirror = mesh.getObjectByName("mirror") as Three.Mesh;
wheel.push(mesh.getObjectByName("wheel1") as Three.Mesh);
wheel.push(mesh.getObjectByName("wheel2") as Three.Mesh);
wheel.push(mesh.getObjectByName("wheel3") as Three.Mesh);
wheel.push(mesh.getObjectByName("wheel4") as Three.Mesh);
mirror.material = new Three.MeshBasicMaterial({
map: renderTarget.texture,
});
});
const ambientLight = new Three.AmbientLight(0xffffff, 1);
scene.add(ambientLight);
rtScene.add(ambientLight.clone());
const wheelRotate = () => {
wheel.forEach((item) => {
item.rotation.z -= 0.05;
});
};
const loop = () => {
wheelRotate();
torus.rotation.x += 0.01;
renderer.setRenderTarget(renderTarget);
renderer.render(rtScene, rtCamera);
renderer.setRenderTarget(null);
renderer.render(scene, camera);
requestAnimationFrame(loop);
};
loop();
部分代码省略了,效果如下:
总之,WebGLRenderTarget是一个比较悬乎的概念,使用中会遇到各种各样的问题,欢迎大家一起讨论学习!
平台声明:以上文章转载于《CSDN》,文章全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,仅作参考。
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/weixin_55100638/article/details/147272193