代码演示问题
(注释/取消注释顶点着色器中的gl_Position
行)
var scene;
var book;
var shaderMaterial;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0x000000);
document.body.appendChild(renderer.domElement);
var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);
window.onresize = function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
window.onresize();
scene = new THREE.Scene();
camera.position.z = 25;
camera.position.y = 15;
scene.add(camera);
var grid = new THREE.GridHelper(100, 10);
scene.add(grid);
var controls = new THREE.OrbitControls(camera);
controls.damping = 0.2;
var lettersPerSide = 16;
function createGlpyhSheet() {
var fontSize = 64;
var c = document.createElement('canvas');
c.width = c.height = fontSize * lettersPerSide;
var ctx = c.getContext('2d');
ctx.font = fontSize + 'px Monospace';
var i = 0;
for (var y = 0; y < lettersPerSide; y++) {
for (var x = 0; x < lettersPerSide; x++, i++) {
var ch = String.fromCharCode(i);
ctx.fillText(ch, x * fontSize, -(8 / 32) * fontSize + (y + 1) * fontSize);
}
}
var tex = new THREE.Texture(c);
tex.flipY = false;
tex.needsUpdate = true;
return tex;
}
function createLabels(textArrays, positions) {
//console.log(textArrays, positions);
var master_geometry = new THREE.Geometry();
for (var k = 0; k < textArrays.length; k++) {
var geo = new THREE.Geometry();
geo.dynamic = true;
var str = textArrays[k];
var vec = positions[k];
//console.log(shaderMaterial);
//console.log('str is', str, 'vec is', vec);
var j = 0,
ln = 0;
for (i = 0; i < str.length; i++) {
//console.log('creating glyph', str[i]);
var code = str.charCodeAt(i);
var cx = code % lettersPerSide;
var cy = Math.floor(code / lettersPerSide);
var oneDotOne = .55;
geo.vertices.push(
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0).add(vec),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0).add(vec),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0).add(vec),
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0).add(vec));
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
var face = new THREE.Face3(i * 4 + 0, i * 4 + 1, i * 4 + 2);
geo.faces.push(face);
face = new THREE.Face3(i * 4 + 0, i * 4 + 2, i * 4 + 3);
geo.faces.push(face);
var ox = (cx + 0.05) / lettersPerSide;
var oy = (cy + 0.05) / lettersPerSide;
var off = 0.9 / lettersPerSide;
geo.faceVertexUvs[0].push([
new THREE.Vector2(ox, oy + off),
new THREE.Vector2(ox + off, oy + off),
new THREE.Vector2(ox + off, oy)]);
geo.faceVertexUvs[0].push([
new THREE.Vector2(ox, oy + off),
new THREE.Vector2(ox + off, oy),
new THREE.Vector2(ox, oy)]);
if (code == 10) {
ln--;
j = 0;
} else {
j++;
}
}
// i can only get this working with merge.
// Building one giant geometry doesn't work for some reason
master_geometry.merge(geo);
}
console.log(shaderMaterial);
shaderMaterial.attributes.labelpos.needsUpdate = true;
book = new THREE.Mesh(
master_geometry,
shaderMaterial);
//book.doubleSided = true;
scene.add(book);
}
var uniforms = {
map: {
type: "t",
value: createGlpyhSheet()
}
};
var attributes = {
labelpos: {
type: 'v3',
value: []
}
};
shaderMaterial = new THREE.ShaderMaterial({
attributes: attributes,
uniforms: uniforms,
vertexShader: document.querySelector('#vertex').textContent,
fragmentShader: document.querySelector('#fragment').textContent
});
shaderMaterial.transparent = true;
shaderMaterial.depthTest = false;
strings = [];
vectors = [];
var sizeOfWorld = 100;
var halfSize = sizeOfWorld * 0.5;
for (var i = 0; i < 500; i++) {
strings.push('test' + i);
var vector = new THREE.Vector3();
vector.x = Math.random() * sizeOfWorld - halfSize;
vector.y = Math.random() * sizeOfWorld - halfSize;
vector.z = Math.random() * sizeOfWorld - halfSize;
vectors.push(vector);
}
console.log('creating labels');
createLabels(strings, vectors);
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate, renderer.domElement);
}
animate();
html {
background-color: #ffffff;
}
* {
margin: 0;
padding: 0;
}
<script src="http://threejs.org/build/three.min.js"></script>
<script src="http://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script id="vertex" type="text/x-glsl-vert">
varying vec2 vUv;
attribute vec3 labelpos;
void main() {
vUv = uv;
// standard gl_Position. Labels stay in the correct place, but do not billboard.
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
// this is the billboarding position as described by:
// http://stackoverflow.com/questions/22053932/three-js-billboard-vertex-shader
//gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) + vec4(position.x, position.y, 0.0, 0.0));
// this gets a little closer
//gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, position.z, 1.0) + vec4(position.x, position.y, 0.0, 0.0));
}
</script>
<script id="fragment" type="text/x-glsl-frag">
varying vec2 vUv;
uniform sampler2D map;
void main() {
vec4 diffuse = texture2D(map, vUv);
vec4 letters = mix(diffuse, vec4(1.0, 1.0, 1.0, diffuse.a), 1.0);
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * letters;
}
</script>
我需要在我的场景中帮助广告标签。我的最后一幕将有数百个标签,我想面对镜头。我无法通过单个网格几何体找到一种方法。我尝试了一些不同的gl_Position
方法来获得广告牌效果:
// standard gl_Position. Labels stay in the correct place, but do not billboard.
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
// this is the billboarding position as described by:
// http://stackoverflow.com/questions/22053932/three-js-billboard-vertex-shader
gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0) + vec4(position.x, position.y, 0.0, 0.0));
// this gets a little closer
gl_Position = projectionMatrix * (modelViewMatrix * vec4(0.0, 0.0, position.z, 1.0) + vec4(position.x, position.y, 0.0, 0.0));
我的想法是将着色器属性发送到每个顶点以协助广告牌计算,这就是为什么我在顶点着色器中有label_pos
属性。
如果每个标签(由字符组成)分别添加到场景中,我可以获得我想要的精确外观。不幸的是,这导致每个渲染循环的绘制调用太多,因此将它们全部添加到单个几何体中的原因。
非常感谢对此的任何帮助,谢谢。
答案 0 :(得分:5)
我想你想要
gl_Position = projectionMatrix *
(modelViewMatrix * vec4(labelpos, 1) +
vec4(position.xy, 0, 0));
并且您不需要将位置添加到顶点
geo.vertices.push(
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));
否则你将两次进入该职位。
因为所有标签都在同一个网格中,所以只有1个绘制调用,这意味着您不会为每个标签获取不同的位置,除非您将其传入(您在标签中但是您没有使用它)
在哪种情况下,modelViewMatrix * vec4(0,0,0,1)
与仅说modelViewMatrix[3]
相同。您所做的只是获取包含所有标签的模型的翻译。如果每个标签都是一个单独的网格并且有自己的矩阵,那么这将起作用,但由于你将它们全部放在一个网格中,它将无法工作。
您的修正是在您已经包含的单独属性中传递每个标签的位置,您只需要使用它。
modelViewMatrix * vec4(labelpos, 1)
获取标签的根
vec4(position.x, position.y, 0.0, 0.0)
添加视图空间中的角落
var scene;
var book;
var shaderMaterial;
var renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setClearColor(0x000000);
document.body.appendChild(renderer.domElement);
var camera = new THREE.PerspectiveCamera(55, 1, 0.1, 40000);
window.onresize = function () {
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
window.onresize();
scene = new THREE.Scene();
camera.position.z = 25;
camera.position.y = 15;
scene.add(camera);
var grid = new THREE.GridHelper(100, 10);
scene.add(grid);
var controls = new THREE.OrbitControls(camera);
controls.damping = 0.2;
var lettersPerSide = 16;
function createGlpyhSheet() {
var fontSize = 64;
var c = document.createElement('canvas');
c.width = c.height = fontSize * lettersPerSide;
var ctx = c.getContext('2d');
ctx.font = fontSize + 'px Monospace';
var i = 0;
for (var y = 0; y < lettersPerSide; y++) {
for (var x = 0; x < lettersPerSide; x++, i++) {
var ch = String.fromCharCode(i);
ctx.fillText(ch, x * fontSize, -(8 / 32) * fontSize + (y + 1) * fontSize);
}
}
var tex = new THREE.Texture(c);
tex.flipY = false;
tex.needsUpdate = true;
return tex;
}
function createLabels(textArrays, positions) {
//console.log(textArrays, positions);
var master_geometry = new THREE.Geometry();
for (var k = 0; k < textArrays.length; k++) {
var geo = new THREE.Geometry();
geo.dynamic = true;
var str = textArrays[k];
var vec = positions[k];
//console.log(shaderMaterial);
//console.log('str is', str, 'vec is', vec);
var j = 0,
ln = 0;
for (i = 0; i < str.length; i++) {
//console.log('creating glyph', str[i]);
var code = str.charCodeAt(i);
var cx = code % lettersPerSide;
var cy = Math.floor(code / lettersPerSide);
var oneDotOne = .55;
geo.vertices.push(
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 0.05, 0),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 0.05, 0),
new THREE.Vector3(j * oneDotOne + 1.05, ln * oneDotOne + 1.05, 0),
new THREE.Vector3(j * oneDotOne + 0.05, ln * oneDotOne + 1.05, 0));
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
shaderMaterial.attributes.labelpos.value.push(vec);
var face = new THREE.Face3(i * 4 + 0, i * 4 + 1, i * 4 + 2);
geo.faces.push(face);
face = new THREE.Face3(i * 4 + 0, i * 4 + 2, i * 4 + 3);
geo.faces.push(face);
var ox = (cx + 0.05) / lettersPerSide;
var oy = (cy + 0.05) / lettersPerSide;
var off = 0.9 / lettersPerSide;
geo.faceVertexUvs[0].push([
new THREE.Vector2(ox, oy + off),
new THREE.Vector2(ox + off, oy + off),
new THREE.Vector2(ox + off, oy)]);
geo.faceVertexUvs[0].push([
new THREE.Vector2(ox, oy + off),
new THREE.Vector2(ox + off, oy),
new THREE.Vector2(ox, oy)]);
if (code == 10) {
ln--;
j = 0;
} else {
j++;
}
}
// i can only get this working with merge.
// Building one giant geometry doesn't work for some reason
master_geometry.merge(geo);
}
console.log(shaderMaterial);
shaderMaterial.attributes.labelpos.needsUpdate = true;
book = new THREE.Mesh(
master_geometry,
shaderMaterial);
//book.doubleSided = true;
scene.add(book);
}
var uniforms = {
map: {
type: "t",
value: createGlpyhSheet()
}
};
var attributes = {
labelpos: {
type: 'v3',
value: []
}
};
shaderMaterial = new THREE.ShaderMaterial({
attributes: attributes,
uniforms: uniforms,
vertexShader: document.querySelector('#vertex').textContent,
fragmentShader: document.querySelector('#fragment').textContent
});
shaderMaterial.transparent = true;
shaderMaterial.depthTest = false;
strings = [];
vectors = [];
var sizeOfWorld = 100;
var halfSize = sizeOfWorld * 0.5;
for (var i = 0; i < 500; i++) {
strings.push('test' + i);
var vector = new THREE.Vector3();
vector.x = Math.random() * sizeOfWorld - halfSize;
vector.y = Math.random() * sizeOfWorld - halfSize;
vector.z = Math.random() * sizeOfWorld - halfSize;
vectors.push(vector);
}
console.log('creating labels');
createLabels(strings, vectors);
function animate() {
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animate, renderer.domElement);
}
animate();
&#13;
html {
background-color: #ffffff;
}
* {
margin: 0;
padding: 0;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/69/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/4862f5f1111346a957ac3e0cb0858be1568d0e03/examples/js/controls/OrbitControls.js"></script>
<script id="vertex" type="text/x-glsl-vert">
varying vec2 vUv;
attribute vec3 labelpos;
void main() {
vUv = uv;
gl_Position = projectionMatrix *
(modelViewMatrix * vec4(labelpos, 1) +
vec4(position.xy, 0, 0));
}
</script>
<script id="fragment" type="text/x-glsl-frag">
varying vec2 vUv;
uniform sampler2D map;
void main() {
vec4 diffuse = texture2D(map, vUv);
vec4 letters = mix(diffuse, vec4(1.0, 1.0, 1.0, diffuse.a), 1.0);
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0) * letters;
}
</script>
&#13;
答案 1 :(得分:1)
还值得研究在Three.js及其SpriteMaterial中如何完成:sprite_vert.glsl
这是一个带注释的代码段:
// optional: pass 2D rotation angle as an uniform
uniform float rotation;
// optional: pass 2D center point as an uniform
uniform vec2 center;
// optional: use this define to scale the model according to distance from the camera
#define USE_SIZEATTENUATION
// [skipped includes]
void main() {
// discard rotation and scale
vec4 mvPosition = modelViewMatrix * vec4( 0.0, 0.0, 0.0, 1.0 );
// extract model's scale
vec2 scale;
scale.x = length( vec3( modelMatrix[ 0 ].x, modelMatrix[ 0 ].y, modelMatrix[ 0 ].z ) );
scale.y = length( vec3( modelMatrix[ 1 ].x, modelMatrix[ 1 ].y, modelMatrix[ 1 ].z ) );
// if not defined, keep model the same size regardless of distance from the camera
#ifndef USE_SIZEATTENUATION
bool isPerspective = isPerspectiveMatrix( projectionMatrix );
if ( isPerspective ) scale *= - mvPosition.z;
#endif
// if center is not passed as uniform, create vec2 center = vec2(0.0);
// aligned with the camera [and scaled]
vec2 alignedPosition = ( position.xy - ( center - vec2( 0.5 ) ) ) * scale;
// if rotation is not passed as uniform, skip the next block
// rotate 2D
vec2 rotatedPosition;
rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;
rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;
// billboard
mvPosition.xy += rotatedPosition;
gl_Position = projectionMatrix * mvPosition;
// [skipped includes]
}