THREE.JS | GLSL 如何将 canvas 纹理拆分为多个平面
THREE.JS | GLSL How to split canvas texture to multiple planes
我有一个小演示,我有多个平面在 1024x1024 区域内堆叠在一起。
任务是均匀分割 1024x1024 动画 canvas 纹理,所以我会得到以下结果:
不是这个:
所以,我必须以某种方式传递给他们的着色器 material 我需要裁剪 canvas 纹理的哪一部分。而且我不知道该怎么做。
当前代码附在此处。
下面的答案很好,但我必须对这些平面使用 glsl,所以这个解决方案可能适用于其他任务,而不是我的。
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
var uniforms = {
markup: { type: 't', value: markupTexture }
};
glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
var plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 640, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 640, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
for(var i = 0; i < 10; i++){
scene.getObjectByName("plane" + i).material.uniforms.markup.needsUpdate = true;
}
//glslMaterial.uniforms.markup.needsUpdate = true;
markupTexture.needsUpdate = true;
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv);
}
</script>
</body>
</html>
您不需要自定义着色器。您只需要为每个平面设置纹理坐标即可。
这里有一个缩放和偏移纹理坐标的函数
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
如果将比例设置为 0.25,0.25 则纹理的 16 分之一将出现在该平面上(就像纹理被分成 4x4 网格一样)
偏移量将移动纹理,其中 1 是纹理的全长,0.5 是长度的一半,0.25 是长度的四分之一
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
/*glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
*/
const simple = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide });
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0.5, 0.5, 0.5, 0.5);
var plane = new THREE.Mesh(geometry, simple);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0, 0, 0.5, 0.5);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
markupTexture.needsUpdate = true;
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
</body>
</html>
Ps: 我把圆圈变大了,所以它碰到了所有的平面,否则 2 个角是纯黄色的,所以很难看出它们是否正确。
参见this article作为几何结构的参考
同样的解决方案也适用于您的自定义着色器
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
var uniforms = {
markup: { type: 't', value: markupTexture }
};
glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0.5, 0.5, 0.5, 0.5);
var plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0, 0, 0.5, 0.5);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
markupTexture.needsUpdate = true;
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv);
}
</script>
</head>
<body>
</body>
</html>
您也可以在着色器中进行相同的 uv 操作,但是您需要为每个平面创建一个新的 material,这样您就可以为每个平面传入不同的偏移量和比例。
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
function createMaterial(offX, offY, scaleX, scaleY) {
var uniforms = {
uvOffset: { value: new THREE.Vector2(offX, offY) },
uvScale: { value: new THREE.Vector2(scaleX, scaleY) },
markup: { type: 't', value: markupTexture }
};
return new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
}
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
var plane = new THREE.Mesh(geometry, createMaterial(0.5, 0.5, 0.5, 0.5));
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.75, 0.25, 0.25, 0.25));
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.5, 0.25, 0.25, 0.25));
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.5, 0, 0.25, 0.25));
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.75, 0, 0.25, 0.25));
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
plane = new THREE.Mesh(geometry, createMaterial(0, 0, 0.5, 0.5));
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.0, 0.5, 0.25, 0.25));
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.25, 0.5, 0.25, 0.25));
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.25, 0.75, 0.25, 0.25));
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.0, 0.75, 0.25, 0.25));
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
markupTexture.needsUpdate = true;
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
uniform vec2 uvOffset;
uniform vec2 uvScale;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv * uvScale + uvOffset);
}
</script>
</body>
</html>
我有一个小演示,我有多个平面在 1024x1024 区域内堆叠在一起。 任务是均匀分割 1024x1024 动画 canvas 纹理,所以我会得到以下结果:
不是这个:
所以,我必须以某种方式传递给他们的着色器 material 我需要裁剪 canvas 纹理的哪一部分。而且我不知道该怎么做。
当前代码附在此处。
下面的答案很好,但我必须对这些平面使用 glsl,所以这个解决方案可能适用于其他任务,而不是我的。
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
var uniforms = {
markup: { type: 't', value: markupTexture }
};
glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
var plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 640, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 640, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
for(var i = 0; i < 10; i++){
scene.getObjectByName("plane" + i).material.uniforms.markup.needsUpdate = true;
}
//glslMaterial.uniforms.markup.needsUpdate = true;
markupTexture.needsUpdate = true;
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv);
}
</script>
</body>
</html>
您不需要自定义着色器。您只需要为每个平面设置纹理坐标即可。
这里有一个缩放和偏移纹理坐标的函数
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
如果将比例设置为 0.25,0.25 则纹理的 16 分之一将出现在该平面上(就像纹理被分成 4x4 网格一样)
偏移量将移动纹理,其中 1 是纹理的全长,0.5 是长度的一半,0.25 是长度的四分之一
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
/*glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
*/
const simple = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide });
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0.5, 0.5, 0.5, 0.5);
var plane = new THREE.Mesh(geometry, simple);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0, 0, 0.5, 0.5);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, simple);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
markupTexture.needsUpdate = true;
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
</body>
</html>
Ps: 我把圆圈变大了,所以它碰到了所有的平面,否则 2 个角是纯黄色的,所以很难看出它们是否正确。
参见this article作为几何结构的参考
同样的解决方案也适用于您的自定义着色器
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
var uniforms = {
markup: { type: 't', value: markupTexture }
};
glslMaterial = new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0.5, 0.5, 0.5, 0.5);
var plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0.25, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.5, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.75, 0, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
offsetUVs(geometry, 0, 0, 0.5, 0.5);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.5, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.25, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
offsetUVs(geometry, 0.0, 0.75, 0.25, 0.25);
plane = new THREE.Mesh(geometry, glslMaterial);
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function offsetUVs(geometry, offU, offV, scaleU, scaleV) {
const off = new THREE.Vector2(offU, offV);
const scale = new THREE.Vector2(scaleU, scaleV);
for(const uvs of geometry.faceVertexUvs[0]) {
for (const uv of uvs) {
uv.multiply(scale);
uv.add(off);
}
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
markupTexture.needsUpdate = true;
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv);
}
</script>
</head>
<body>
</body>
</html>
您也可以在着色器中进行相同的 uv 操作,但是您需要为每个平面创建一个新的 material,这样您就可以为每个平面传入不同的偏移量和比例。
var renderer, scene, camera, controls, glslMaterial, uniforms, canvas, ctx, markupTexture, t = 0.0;
inits();
function inits(){
canvas = document.createElement("canvas")
canvas.width = 1024 * 2;
canvas.height = 1024 * 2;
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#00FFFF";
ctx.fillRect(0, 0, canvas.width, canvas.height);
markupTexture = new THREE.CanvasTexture(canvas);
//markupTexture.flipX = false;
//markupTexture.flipY = false;
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x000000);
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xFFFFFF, 0x080820, 2.0 );
scene.add( light );
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 5120 );
camera.position.set(-200, 400, 400);
controls = new THREE.OrbitControls(camera, renderer.domElement);
var markupGeometry = new THREE.PlaneGeometry(1024, 1024, 64, 64);
var markupPlane = new THREE.Mesh(markupGeometry, new THREE.MeshBasicMaterial({ color: 0xFFFFFF, map: markupTexture, side: THREE.DoubleSide }));
markupPlane.rotation.set(-Math.PI / 2, 0, 0);
markupPlane.position.set(0, 1, 0);
//scene.add(markupPlane);
function createMaterial(offX, offY, scaleX, scaleY) {
var uniforms = {
uvOffset: { value: new THREE.Vector2(offX, offY) },
uvScale: { value: new THREE.Vector2(scaleX, scaleY) },
markup: { type: 't', value: markupTexture }
};
return new THREE.ShaderMaterial({
uniforms: uniforms,
vertexShader: document.getElementById('vertexShader').textContent,
fragmentShader: document.getElementById('fragmentShader').textContent
//side: new THREE.DoubleSide
});
}
var geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
var plane = new THREE.Mesh(geometry, createMaterial(0.5, 0.5, 0.5, 0.5));
plane.name = "plane0";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(256, 0, -256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.75, 0.25, 0.25, 0.25));
plane.name = "plane1";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.5, 0.25, 0.25, 0.25));
plane.name = "plane2";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.5, 0, 0.25, 0.25));
plane.name = "plane3";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(128, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.75, 0, 0.25, 0.25));
plane.name = "plane4";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(384, 0, 384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(512, 512, 64, 64);
plane = new THREE.Mesh(geometry, createMaterial(0, 0, 0.5, 0.5));
plane.name = "plane5";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-256, 0, 256)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.0, 0.5, 0.25, 0.25));
plane.name = "plane6";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.25, 0.5, 0.25, 0.25));
plane.name = "plane7";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -128)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.25, 0.75, 0.25, 0.25));
plane.name = "plane8";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-128, 0, -384)
scene.add(plane);
geometry = new THREE.PlaneGeometry(256, 256, 32, 32);
plane = new THREE.Mesh(geometry, createMaterial(0.0, 0.75, 0.25, 0.25));
plane.name = "plane9";
plane.rotation.set(-Math.PI / 2, 0, 0);
plane.position.set(-384, 0, -384)
scene.add(plane);
window.addEventListener( "resize", onWindowResize, false );
animate();
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
ctx.imageSmoothingEnabled = true;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#FFFF00";
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(1024, 1024, 940, 0, 2 * Math.PI);
ctx.strokeStyle = "#FF00FF";
ctx.lineWidth = 16.0;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(1024, 1024);
ctx.arc(1024, 1024, 940, t - Math.PI / 8, t + Math.PI / 8, false);
ctx.lineTo(1024, 1024);
ctx.fillStyle = "#FF00FF";
ctx.fill();
controls.update();
requestAnimationFrame( animate );
renderer.render( scene, camera );
markupTexture.needsUpdate = true;
t += 0.05;
}
body { margin: 0px; }
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title></title>
<script src="https://unpkg.com/three@0.85.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.85.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body>
<script id="vertexShader" type="x-shader/x-vertex">
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
</script>
<script id="fragmentShader" type="x-shader/x-fragment">
uniform sampler2D markup;
uniform vec2 uvOffset;
uniform vec2 uvScale;
varying vec2 vUv;
void main() {
gl_FragColor = texture2D(markup, vUv * uvScale + uvOffset);
}
</script>
</body>
</html>