在 A-FRAME 中创建简化的反射自定义着色器
Create a simplified reflective custom shader in A-FRAME
所以我不可避免地走上了 A-FRAME 中自定义着色器的道路。在真正研究这个时,考虑到我可以被认为是这项技术的新手,我遇到了各种复杂且不太清晰的解决方案。
例如:https://rawgit.com/mrdoob/three.js/master/examples/webgl_materials_cubemap_balls_reflection.html,提出的问题比答案还要多。
但是,当我访问这个示例:https://stemkoski.github.io/Three.js/Reflection.html 时,我确实注意到了一种不同的方法,它似乎更加简化了。
这促使我进一步研究 CubeCameras 以及 threejs 如何将其用作着色器以模拟反射率。
//Create cube camera
var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 );
scene.add( cubeCamera );
//Create car
var chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeCamera.renderTarget } );
var car = new Mesh( carGeometry, chromeMaterial );
scene.add( car );
//Update the render target cube
car.setVisible( false );
cubeCamera.position.copy( car.position );
cubeCamera.updateCubeMap( renderer, scene );
//Render the scene
car.setVisible( true );
renderer.render( scene, camera );
现在的问题是,如何将其转化为 A-FRAME?我尝试了以下方法:
AFRAME.registerComponent('chromeThing', {
schema: {
???
},
init: function () {
var el = this.el; // Entity.
var cubeCam = document.querySelector('#cubeCam'); //There is an <a-entity camera id="cubeCam">
var mirrorCubeMaterial = new THREE.MeshBasicMaterial( { envMap: cubeCam.renderTarget } );
mirrorCube = new THREE.Mesh( el, mirrorCubeMaterial );
el.setObject3D('mesh', mirrorCube);
}
});
您可能已经注意到,我不确定这个方案是什么。
另外,这应该是着色器还是组件? (我怎样才能最好地使用它)
编辑:
在@ngokevin 回复后,我想出了这段代码,但仍然没有给我想要的结果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chrome box</title>
<meta name="description" content="Physically-Based Materials - A-Frame">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/dist/kframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/components/reverse-look-controls/index.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<img id="equi1" crossorigin src="https://rawgit.com/aframevr/aframe/master/examples/boilerplate/panorama/puydesancy.jpg" preload="auto">
</a-assets>
<!-- MAIN CAMERA -->
<a-entity id="mainCam" camera="userHeight: 1.6" reverse-look-controls wasd-controls></a-entity>
<!-- CMAERA FOR TEXTURE (?) -->
<a-entity id="cubecamera" camera near="0.1" far="5000" fov="512"></a-entity>
<!-- SKY DOME TO REFLECT ON BOX -->
<a-sky src="#equi1"></sky>
<!-- MAKE THIS BOX CHROME -->
<a-box id="theCube" camera-envmap-material="#cubecamera" color="tomato" depth="2" height="4" width="5" position="0 0 -3" materials=""></a-box>
<!-- THIS BOX IS MOVING AND SHOULD REFLECT ON THE CHROME BOX -->
<a-box id="reflector" color="red" position="0 0 20">
<a-animation attribute="rotation" dur="10000" fill="forwards" to="0 360 0" repeat="indefinite"></a-animation>
</a-box>
</a-scene>
</body>
<script>
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
// Selector type so we do `<a-entity camera-envmap-material="#cubecamera">`.
schema: {
type: 'selector'
},
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
// Set envmap on existing material.
// This assumes you have a CubeCamera component that does `setObject3D('cube-camera', new THREE.CubeCamera)`.
material.envMap = cameraEl.getObject3D('camera').renderTarget;
material.needsUpdate = true;
}
});
</script>
</html>
更新#2
根据@ngokevin 的建议,我现在就在这里,但是,我无法更新组件。问题似乎是将变量传递给 tick:function()
,我不断收到 Uncaught TypeError: Cannot read property 'renderTarget' of undefined
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chrome box</title>
<meta name="description" content="Physically-Based Materials - A-Frame">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/dist/kframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/components/reverse-look-controls/index.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<img id="equi1" crossorigin src="https://rawgit.com/aframevr/aframe/master/examples/boilerplate/panorama/puydesancy.jpg" preload="auto">
</a-assets>
<!-- MAIN CAMERA -->
<a-entity id="mainCam" camera="userHeight: 1.6" reverse-look-controls wasd-controls></a-entity>
<!-- SKY DOME TO REFLECT ON BOX -->
<a-sky src="#equi1"></sky>
<!-- MAKE THIS BOX CHROME -->
<a-box id="theCube" camera-envmap-material depth="2" height="4" width="5" position="0 0 -3" metalness="1">
</a-box>
<!-- MAKE THIS ROTATE AND REFLECT ON CHROME -->
<a-box id="reflector" color="red" position="0 0 10">
<a-animation attribute="rotation" dur="10000" fill="forwards" to="0 360 0" repeat="indefinite"></a-animation>
</a-box>
</a-scene>
</body>
<script>
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
init: function(data) {
this.cameraEl = this.data;
this.material = this.el.getObject3D('mesh').material;
this.theElement = this.el;
this.theScene = this.theElement.sceneEl;
if (!this.theScene.renderStarted) {
this.theScene.addEventListener('renderstart', this.init.bind(this));
return;
}
this.cubeCamera = new THREE.CubeCamera( 0.1, 5000, 512);
this.cubeCamera.position.set(5, 2, 4);
this.cubeCamera.updateCubeMap( this.theScene.renderer, this.theScene.object3D );
this.theElement.setObject3D('cube-camera', this.cubeCamera);
this.material.envMap = this.cubeCamera.renderTarget;
this.material.needsUpdate = true;
},
tick:function(){
this.material.envMap = this.cubeCamera.renderTarget;
this.material.needsUpdate = true;
}
});
</script>
</html>
组件可以在现有 material 上设置属性。 shader更多的是注册shaders/materials,但是A-Frame已经有basic/standard material了。并且您希望架构中的选择器 属性。:
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
// Selector type so we do `<a-entity camera-envmap-material="#cubecamera">`.
schema: {
type: 'selector'
},
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
// Set envmap on existing material.
// This assumes you have a CubeCamera component that does `setObject3D('cube-camera', new THREE.CubeCamera)`.
material.envMap = cameraEl.getObject3D('camera').renderTarget;
material.needsUpdate = true;
}
});
更新
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
if (!this.el.sceneEl.renderStarted) {
this.el.sceneEl.addEventListener('renderstart', this.init.bind(this));
return;
}
var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 );
cubeCamera.position.set(5, 2, 4);
cubeCamera.updateCubeMap( this.el.sceneEl.renderer, this.el.sceneEl.object3D );
this.el.setObject3D('cube-camera', cubeCamera);
material.envMap = cubeCamera.renderTarget;
material.needsUpdate = true;
}
});
所以我不可避免地走上了 A-FRAME 中自定义着色器的道路。在真正研究这个时,考虑到我可以被认为是这项技术的新手,我遇到了各种复杂且不太清晰的解决方案。
例如:https://rawgit.com/mrdoob/three.js/master/examples/webgl_materials_cubemap_balls_reflection.html,提出的问题比答案还要多。
但是,当我访问这个示例:https://stemkoski.github.io/Three.js/Reflection.html 时,我确实注意到了一种不同的方法,它似乎更加简化了。
这促使我进一步研究 CubeCameras 以及 threejs 如何将其用作着色器以模拟反射率。
//Create cube camera
var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 );
scene.add( cubeCamera );
//Create car
var chromeMaterial = new THREE.MeshLambertMaterial( { color: 0xffffff, envMap: cubeCamera.renderTarget } );
var car = new Mesh( carGeometry, chromeMaterial );
scene.add( car );
//Update the render target cube
car.setVisible( false );
cubeCamera.position.copy( car.position );
cubeCamera.updateCubeMap( renderer, scene );
//Render the scene
car.setVisible( true );
renderer.render( scene, camera );
现在的问题是,如何将其转化为 A-FRAME?我尝试了以下方法:
AFRAME.registerComponent('chromeThing', {
schema: {
???
},
init: function () {
var el = this.el; // Entity.
var cubeCam = document.querySelector('#cubeCam'); //There is an <a-entity camera id="cubeCam">
var mirrorCubeMaterial = new THREE.MeshBasicMaterial( { envMap: cubeCam.renderTarget } );
mirrorCube = new THREE.Mesh( el, mirrorCubeMaterial );
el.setObject3D('mesh', mirrorCube);
}
});
您可能已经注意到,我不确定这个方案是什么。 另外,这应该是着色器还是组件? (我怎样才能最好地使用它)
编辑: 在@ngokevin 回复后,我想出了这段代码,但仍然没有给我想要的结果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chrome box</title>
<meta name="description" content="Physically-Based Materials - A-Frame">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/dist/kframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/components/reverse-look-controls/index.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<img id="equi1" crossorigin src="https://rawgit.com/aframevr/aframe/master/examples/boilerplate/panorama/puydesancy.jpg" preload="auto">
</a-assets>
<!-- MAIN CAMERA -->
<a-entity id="mainCam" camera="userHeight: 1.6" reverse-look-controls wasd-controls></a-entity>
<!-- CMAERA FOR TEXTURE (?) -->
<a-entity id="cubecamera" camera near="0.1" far="5000" fov="512"></a-entity>
<!-- SKY DOME TO REFLECT ON BOX -->
<a-sky src="#equi1"></sky>
<!-- MAKE THIS BOX CHROME -->
<a-box id="theCube" camera-envmap-material="#cubecamera" color="tomato" depth="2" height="4" width="5" position="0 0 -3" materials=""></a-box>
<!-- THIS BOX IS MOVING AND SHOULD REFLECT ON THE CHROME BOX -->
<a-box id="reflector" color="red" position="0 0 20">
<a-animation attribute="rotation" dur="10000" fill="forwards" to="0 360 0" repeat="indefinite"></a-animation>
</a-box>
</a-scene>
</body>
<script>
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
// Selector type so we do `<a-entity camera-envmap-material="#cubecamera">`.
schema: {
type: 'selector'
},
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
// Set envmap on existing material.
// This assumes you have a CubeCamera component that does `setObject3D('cube-camera', new THREE.CubeCamera)`.
material.envMap = cameraEl.getObject3D('camera').renderTarget;
material.needsUpdate = true;
}
});
</script>
</html>
更新#2
根据@ngokevin 的建议,我现在就在这里,但是,我无法更新组件。问题似乎是将变量传递给 tick:function()
,我不断收到 Uncaught TypeError: Cannot read property 'renderTarget' of undefined
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Chrome box</title>
<meta name="description" content="Physically-Based Materials - A-Frame">
<meta name="apple-mobile-web-app-capable" content="yes">
<script src="https://aframe.io/releases/0.3.0/aframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/dist/kframe.min.js"></script>
<script src="https://rawgit.com/ngokevin/kframe/master/components/reverse-look-controls/index.js"></script>
</head>
<body>
<a-scene>
<a-assets>
<img id="equi1" crossorigin src="https://rawgit.com/aframevr/aframe/master/examples/boilerplate/panorama/puydesancy.jpg" preload="auto">
</a-assets>
<!-- MAIN CAMERA -->
<a-entity id="mainCam" camera="userHeight: 1.6" reverse-look-controls wasd-controls></a-entity>
<!-- SKY DOME TO REFLECT ON BOX -->
<a-sky src="#equi1"></sky>
<!-- MAKE THIS BOX CHROME -->
<a-box id="theCube" camera-envmap-material depth="2" height="4" width="5" position="0 0 -3" metalness="1">
</a-box>
<!-- MAKE THIS ROTATE AND REFLECT ON CHROME -->
<a-box id="reflector" color="red" position="0 0 10">
<a-animation attribute="rotation" dur="10000" fill="forwards" to="0 360 0" repeat="indefinite"></a-animation>
</a-box>
</a-scene>
</body>
<script>
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
init: function(data) {
this.cameraEl = this.data;
this.material = this.el.getObject3D('mesh').material;
this.theElement = this.el;
this.theScene = this.theElement.sceneEl;
if (!this.theScene.renderStarted) {
this.theScene.addEventListener('renderstart', this.init.bind(this));
return;
}
this.cubeCamera = new THREE.CubeCamera( 0.1, 5000, 512);
this.cubeCamera.position.set(5, 2, 4);
this.cubeCamera.updateCubeMap( this.theScene.renderer, this.theScene.object3D );
this.theElement.setObject3D('cube-camera', this.cubeCamera);
this.material.envMap = this.cubeCamera.renderTarget;
this.material.needsUpdate = true;
},
tick:function(){
this.material.envMap = this.cubeCamera.renderTarget;
this.material.needsUpdate = true;
}
});
</script>
</html>
组件可以在现有 material 上设置属性。 shader更多的是注册shaders/materials,但是A-Frame已经有basic/standard material了。并且您希望架构中的选择器 属性。:
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
// Selector type so we do `<a-entity camera-envmap-material="#cubecamera">`.
schema: {
type: 'selector'
},
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
// Set envmap on existing material.
// This assumes you have a CubeCamera component that does `setObject3D('cube-camera', new THREE.CubeCamera)`.
material.envMap = cameraEl.getObject3D('camera').renderTarget;
material.needsUpdate = true;
}
});
更新
AFRAME.registerComponent('camera-envmap-material', {
dependencies: ['material'],
init: function () {
var cameraEl = this.data;
var material = this.el.getObject3D('mesh').material;
if (!this.el.sceneEl.renderStarted) {
this.el.sceneEl.addEventListener('renderstart', this.init.bind(this));
return;
}
var cubeCamera = new THREE.CubeCamera( 1, 100000, 128 );
cubeCamera.position.set(5, 2, 4);
cubeCamera.updateCubeMap( this.el.sceneEl.renderer, this.el.sceneEl.object3D );
this.el.setObject3D('cube-camera', cubeCamera);
material.envMap = cubeCamera.renderTarget;
material.needsUpdate = true;
}
});