three.js - billboarding vertices in the vertex shader -
code demonstrating issue (comment/uncomment out gl_position
lines in vertex shader)
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 = 0; (var y = 0; y < lettersperside; y++) { (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(); (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; (i = 0; < 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, * 4 + 1, * 4 + 2); geo.faces.push(face); face = new three.face3(i * 4 + 0, * 4 + 2, * 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++; } } // can working merge. // building 1 giant geometry doesn't work 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; (var = 0; < 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 correct place, not billboard. gl_position = projectionmatrix * modelviewmatrix * vec4( position, 1.0 ); // billboarding position 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)); // gets 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>
i need billboarding labels in scene. final scene have hundreds of labels want face camera. cannot figure out way of doing via single mesh geometry. i've tried few different gl_position
methods billboarding look:
// standard gl_position. labels stay in correct place, not billboard. gl_position = projectionmatrix * modelviewmatrix * vec4( position, 1.0 ); // billboarding position 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)); // gets little closer gl_position = projectionmatrix * (modelviewmatrix * vec4(0.0, 0.0, position.z, 1.0) + vec4(position.x, position.y, 0.0, 0.0));
my thinking send shader attribute each vertex assist billboarding calculation, that's why have label_pos
attribute in vertex shader.
i can exact , feel want if each label (made of characters) added scene separately. unfortunately results in many draw calls per render loop, hence reason adding them single geometry.
any assistance on appreciated, thanks.
i think want
gl_position = projectionmatrix * (modelviewmatrix * vec4(labelpos, 1) + vec4(position.xy, 0, 0));
and need not add in position vertices
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));
otherwise you'd putting in position twice.
because labels in same mesh there's 1 draw call means won't different location each label unless pass in (which in labelpos weren't using it)
in case modelviewmatrix * vec4(0,0,0,1)
same saying modelviewmatrix[3]
you're doing getting translation of model contains labels. work if each label separate mesh , had own matrix since you've put them in 1 mesh won't work.
your fix pass in location of each label in separate attribute had included, needed use it.
modelviewmatrix * vec4(labelpos, 1)
gets root of label
vec4(position.x, position.y, 0.0, 0.0)
adds in corners in view space
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 = 0; (var y = 0; y < lettersperside; y++) { (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(); (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; (i = 0; < 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, * 4 + 1, * 4 + 2); geo.faces.push(face); face = new three.face3(i * 4 + 0, * 4 + 2, * 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++; } } // can working merge. // building 1 giant geometry doesn't work 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; (var = 0; < 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; 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>
Comments
Post a Comment