How to apply WebGL instancing to Three.js Wireframe example - three.js

The Three.js WebGL-based wireframe example utilizes the BufferGeometry with BufferAttribute design pattern.
How would you translate the example for InstancedBufferGeometry with InstancedBufferAttribute ; say two instances (copies) of the wirefames?
Note: This question is a clarification/simplification of a previous question that will hopefully benefit a lot of Three.js/WebGL programmers developing performance optimization techniques.

See this codepen for a simplified version of the wireframe example with instancing; animation from the original example was removed for simplicity.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Adopted from three.js webgl - materials - wireframe</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
attribute vec3 offset;
void main() {
vec3 newPosition = position + offset;
gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
void main() {
gl_FragColor.rgb = vec3(1.0);
gl_FragColor.a = 1.0;
}
</script>
<script>
var camera, scene, renderer;
init();
renderer.render( scene, camera );
function init() {
var material, mesh;
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 700;
scene = new THREE.Scene();
var instances = 3;
var size = 100;
var spacing = 50;
var offsets = [];
var geometry = new THREE.InstancedBufferGeometry().copy(new THREE.BoxBufferGeometry( size, size,size ))
for (var i = 0; i < instances; i++) {
offsets = offsets.concat( fnGetOffsets( geometry, (-1 *(size + spacing)) + (i * (size + spacing)) ) );
}
geometry.addAttribute( 'offset', new THREE.InstancedBufferAttribute(new Float32Array( offsets ) , 3 ) );
var material = new THREE.ShaderMaterial( {
uniforms: {},
wireframe: true,
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
material.extensions.derivatives = true;
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// events
window.addEventListener( 'resize', onWindowResize, false );
function fnGetOffsets( geometry,offsetX ) {
var offset = new THREE.Vector3( offsetX, 0, 0 );
var position = geometry.attributes.position;
var offsets = [];
for ( var i = 0; i < position.count; i ++ ) {
offsets.push(offsetX,0,0)
}
return offsets;
} //fnGetOffsets
} //init
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
</body>
</html>

Related

The marker is not showing

I'm trying to add markers to rotating globe object with Three.js - the globe is showing, but the markers is not displayed. I don't know why this happens, the console.log dont throw any errors to debug anything.
Can someone help me find the root to this issue and correct it?
The code inside the index.html file is down below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="js/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
</head>
<body>
<div id="threejs" style="position: absolute; left:0px; top:0px"></div>
<script>
var boards = [];
var texture = [];
var matTexture = [];
var camera, scene, renderer, controls, container, materialBoard, boardGeo,materialSph, sphGeo, sphere,picGeo;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 300000 );
scene.add( camera );
camera.position.set( 0, 1500, 5000);
renderer = new THREE.WebGLRenderer( { antialias:true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xeeeeee );
container = document.getElementById( 'threejs' );
container.appendChild( renderer.domElement );
controls = new THREE.OrbitControls( camera, renderer.domElement );
var loader = new THREE.TextureLoader();
texture[0] = loader.load( "test.png" );
matTexture[0] = new THREE.MeshBasicMaterial( { color:0xffffff, map: texture[0], transparent:true, opacity:1, wireframe:false} );
// ball
materialSph = new THREE.MeshBasicMaterial( { color: 0x0099dd, transparent: true, opacity: 0.8, wireframe:false } );
sphGeo = new THREE.SphereGeometry(1200, 8, 8);
sphere = new THREE.Mesh( sphGeo, materialSph );
scene.add( sphere );
// board for some things
materialBoard = new THREE.MeshBasicMaterial({color:0xdddddd, transparent:true, opacity:0.4, wireframe: true });
boardGeo = new THREE.PlaneGeometry(500,500);
picGeo = new THREE.PlaneGeometry( 500, 500 );
const vertices = sphGeo.attributes.position.array;
console.log(vertices);
var board = new THREE.Mesh( boardGeo, materialBoard ); // board for pictures etc.
var pic = new THREE.Mesh( picGeo, matTexture[ 0 ] );
board.add( pic ) ; // add picture to the board
boards.push( board );
scene.add( boards[ 0 ] );
boards[ 0 ].position.set( 1 * vertices[ 10 ].x , 0.5 * vertices[ 2 ].y, 1.8 * vertices[ 10 ].z );
}
function animate() {
requestAnimationFrame( animate );
for( var n = 0; n < boards.length; n ++ ) {
//boards[ n ].lookAt( camera.position );
boards[ n ].quaternion.copy( camera.quaternion );
}
renderer.render( scene, camera );
controls.update();
}
</script>
</body>
</html>

WebGL shader attribute is not being passed accurately

I'm attempting to adopt the threejs example for wireframes ( https://threejs.org/examples/?q=wire#webgl_materials_wireframe ) using WebGL instancing.
This simple reproduction codepen ( https://codepen.io/ubermario/pen/gzByjP?editors=1000 ) shows that a wireframed cube is rendered when a 'center' attribute is passed to the vertex/fragment shaders with THREE.BufferAttribute.
However, it is not renedered as wireframe when the 'center' attribute is passed to the same shaders with THREE.InstancedBufferAttribute.
No errors are generated. Ideas?
<html lang="en">
<head>
<title>Adopted from three.js webgl - materials - wireframe</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
attribute vec3 center;
varying vec3 vCenter;
void main() {
vCenter = center;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vCenter;
float edgeFactorTri() {
vec3 d = fwidth( vCenter.xyz );
vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );
return min( min( a3.x, a3.y ), a3.z );
}
void main() {
gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.0 ), edgeFactorTri() );
gl_FragColor.a = 1.0;
}
</script>
<script>
var camera, scene, renderer;
init();
renderer.render( scene, camera );
function init() {
var bufferGeometry, material, mesh;
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 700;
scene = new THREE.Scene();
var geometry1 = new THREE.BoxBufferGeometry( 100, 100,100 );
geometry1.addAttribute( 'center', new THREE.BufferAttribute( fnGetFloat32ArrayCenters( geometry1 ), 3 ) );
var material_1 = new THREE.ShaderMaterial( {
uniforms: {},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
material_1.extensions.derivatives = true;
mesh1 = new THREE.Mesh( geometry1, material_1 );
mesh1.position.x = -100;
scene.add( mesh1 );
var bufferGeometry = new THREE.BoxBufferGeometry( 100, 100,100 );
var geometry2 = new THREE.InstancedBufferGeometry();
geometry2.index = bufferGeometry.index;
geometry2.attributes.position = bufferGeometry.attributes.position;
geometry2.attributes.uv = bufferGeometry.attributes.uv;
//Now with instancing
geometry2.addAttribute( 'center', new THREE.InstancedBufferAttribute( fnGetFloat32ArrayCenters( geometry2 ), 3 ) );
var material_2 = new THREE.ShaderMaterial( {
uniforms: {},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent
} );
material_2.extensions.derivatives = true;
mesh2 = new THREE.Mesh( geometry2, material_2 );
mesh2.position.x = 100;
scene.add( mesh2 );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// events
window.addEventListener( 'resize', onWindowResize, false );
function fnGetFloat32ArrayCenters( geometry ) {
var vectors = [
new THREE.Vector3( 1, 0, 0 ),
new THREE.Vector3( 0, 1, 0 ),
new THREE.Vector3( 0, 0, 1 )
];
var position = geometry.attributes.position;
var centers = new Float32Array( position.count * 3 );
for ( var i = 0, l = position.count; i < l; i ++ ) {
vectors[ i % 3 ].toArray( centers, i * 3 );
}
return centers;
} //fnGetFloat32ArrayCenters
} //init
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
</body>
</html>
Solution: The problem appears to be a bug in three.js r83 with meshes created on BoxBufferGeometry vs InstancedBufferGeometry; see the updated codepen that illustrates this in the comments.
The wireframe attribute for material_1 is not applied when its value is false. It appears to be 'stuck' on 'true'; forcing it to 'false' will have no affect on the first cube.
This lead to the misperception that the cube based on material_2 was in error when it was right all along; because it was respecting the default wireframe attribute value of 'false'. When set to 'true' it will display as a wireframed cube.
<html lang="en">
<head>
<title>Adopted from three.js webgl - materials - wireframe</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.js"></script>
<script type="x-shader/x-vertex" id="vertexShader">
attribute vec3 center;
varying vec3 vCenter;
void main() {
vCenter = center;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script type="x-shader/x-fragment" id="fragmentShader">
varying vec3 vCenter;
float edgeFactorTri() {
vec3 d = fwidth( vCenter.xyz );
vec3 a3 = smoothstep( vec3( 0.0 ), d * 1.5, vCenter.xyz );
return min( min( a3.x, a3.y ), a3.z );
}
void main() {
gl_FragColor.rgb = mix( vec3( 1.0 ), vec3( 0.0 ), edgeFactorTri() );
gl_FragColor.a = 1.0;
}
</script>
<script>
var camera, scene, renderer;
init();
renderer.render( scene, camera );
function init() {
var bufferGeometry, material, mesh;
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 700;
scene = new THREE.Scene();
var geometry1 = new THREE.BoxBufferGeometry( 100, 100,100 );
geometry1.addAttribute( 'center', new THREE.BufferAttribute( fnGetFloat32ArrayCenters( geometry1 ), 3 ) );
var material_1 = new THREE.ShaderMaterial( {
uniforms: {},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
wireframe: false //bug:For r83/three.js, property is not applied when the THREE.Mesh constructor is invoked. https://threejs.org/docs/index.html#api/materials/ShaderMaterial.wireframe
} );
material_1.extensions.derivatives = true;
mesh1 = new THREE.Mesh( geometry1, material_1 ); //bug: material_1's wireframe:false property is not applied for r83/three.js
mesh1.position.x = -100;
scene.add( mesh1 );
var bufferGeometry = new THREE.BoxBufferGeometry( 100, 100,100 );
var geometry2 = new THREE.InstancedBufferGeometry();
geometry2.index = bufferGeometry.index;
geometry2.attributes.position = bufferGeometry.attributes.position;
geometry2.attributes.uv = bufferGeometry.attributes.uv;
//Now with instancing
geometry2.addAttribute( 'center', new THREE.InstancedBufferAttribute( fnGetFloat32ArrayCenters( geometry2 ), 3 ) );
var material_2 = new THREE.ShaderMaterial( {
uniforms: {},
vertexShader: document.getElementById( 'vertexShader' ).textContent,
fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
wireframe:true //See: documentation: https://threejs.org/docs/index.html#api/materials/ShaderMaterial.wireframe
} );
material_2.extensions.derivatives = true;
mesh2 = new THREE.Mesh( geometry2, material_2 );
mesh2.position.x = 100;
scene.add( mesh2 );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// events
window.addEventListener( 'resize', onWindowResize, false );
function fnGetFloat32ArrayCenters( geometry ) {
var vectors = [
new THREE.Vector3( 1, 0, 0 ),
new THREE.Vector3( 0, 1, 0 ),
new THREE.Vector3( 0, 0, 1 )
];
var position = geometry.attributes.position;
var centers = new Float32Array( position.count * 3 );
for ( var i = 0, l = position.count; i < l; i ++ ) {
vectors[ i % 3 ].toArray( centers, i * 3 );
}
return centers;
} //fnGetFloat32ArrayCenters
} //init
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
</script>
</body>
</html>

Threejs, using ShaderMaterial to fake a hole in geometries

In Release 53 of Three.js I could fake a hole in a geometry with another geometry using a shader material with
vertex:void main() {
gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);
}
and
fragment:void main() {
gl_FragColor = vec4(1.0, 0.0, 1.0, 0.0); //alpha is zero
}
I could peep through the 'hole' and saw objects behind. Since release 54 I just see the inner object white, I can't peep through anymore.
How can I get it work again?
my complete sample:
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometry - cube</title>
<meta charset="utf-8">
<style>
body {
margin: 0px;
background-color: #DDFFDD;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/old/three_53.js"></script>
<script src="../build/old/controls/TrackballControls_53.js"></script>
<script>
var camera, scene, renderer, controls, pointLight;
init();
animate();
function init() {
renderer = new THREE.WebGLRenderer({antialias: true, alpha: true});
//renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0x505050 ) );
pointLight = new THREE.PointLight(0xFFFFFF, 0.9);
scene.add(pointLight);
var mainGroup = new THREE.Object3D();
var geometry = new THREE.CubeGeometry( 100, 100, 10 );
var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0xaaaaaa } ) );
//mesh.renderOrder = 2;
var geometry2 = new THREE.CubeGeometry( 50, 50, 11 );
var material2 = new THREE.ShaderMaterial({vertexShader:'void main() { gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);}', fragmentShader:'void main() { gl_FragColor = vec4(1.0, 0.0, 1.0, 0.0);}'});
var innerGroup = new THREE.Object3D();
var mesh2 = new THREE.Mesh( geometry2, material2 );
//mesh2.renderOrder = 1;
mainGroup.add( mesh );
innerGroup.add(mesh2);
mainGroup.add( innerGroup );
//
var geometry3 = new THREE.SphereGeometry( 50);
var mesh3 = new THREE.Mesh( geometry3, new THREE.MeshLambertMaterial( { color: 0x00ff00 } ) );
mesh3.position.z = -200;
//mesh2.renderOrder = 3;
mainGroup.add( mesh3 );
scene.add(mainGroup);
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
controls.update();
pointLight.position.set(camera.position.x, camera.position.y, camera.position.z);
renderer.render( scene, camera );
}
</script>
</body>
It only works, if the 'hole-object' with the shader material is in a group.
If this worked before it's merely by accident. But you can, in 71, get this to work if you use renderOrder:
holeObj.renderOrder = 1;
bgObj.renderOrder = 2;
Now, if holeObj is in front of bgObj then in normal cases you will see through the bgObj. This is because holeObj will still write to the Z-buffer, when it draws its transparent pixels. The bgObj will be masked from that location. But this will only work for a particular view direction without some careful management of the sorting.

How to make a flat ring in Three.js?

I was able to make a donut with Three.js using THREE.TorusGeometry. But I can't get it to look like a flat ring like the ones in these pictures:
http://www.google.com/imgres?imgurl=http://www.titanjewellery.co.uk/Mens/TI21-Titanium-8mm-Flat-Brushed-Ring.jpg&imgrefurl=http://www.titanjewellery.co.uk/Mens/8mm-Brushed-Titanium-Flat-Ring.html&h=301&w=232&sz=16&tbnid=LCN7eQuo2wyG_M:&tbnh=90&tbnw=69&zoom=1&usg=__3vayMvDy26tsj2hwvCK9SsYwVwY=&docid=ZMdcBBBQOzMSoM&sa=X&ei=pEhsUeL4FKWJiAKCzIHYCQ&ved=0CEAQ9QEwBA&dur=1660
Here's what my donut looks like:
Is there another Three.js geometry that can generate a flat ring (right with flat inner and outer walls)? Or another way of going about this?
Thanks for any pointers you can share! :)
Update:
The code and dependencies were taken from:
http://mrdoob.github.io/three.js/examples/misc_controls_trackball.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - trackball controls</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
color: #000;
font-family:Monospace;
font-size:13px;
text-align:center;
font-weight: bold;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color:#000;
position: absolute;
top: 0px; width: 100%;
padding: 5px;
}
a {
color: red;
}
</style>
</head>
<body>
<div id="container"></div>
<div id="info">
three.js - trackball controls example</br>MOVE mouse & press LEFT/A: rotate, MIDDLE/S: zoom, RIGHT/D: pan
</div>
<script src="three.min.js"></script>
<script src="TrackballControls.js"></script>
<script src="Detector.js"></script>
<script src="stats.min.js"></script>
<script>
if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
var container, stats;
var camera, controls, scene, renderer;
var cross;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
var radius = 100;
var tubeRadius = 50;
var radialSegments = 8 * 10;
var tubularSegments = 6 * 15;
var arc = Math.PI * 2;
var geometry = new THREE.TorusGeometry( radius, tubeRadius, radialSegments, tubularSegments, arc );
var material = new THREE.MeshLambertMaterial( { color:0xffffff, shading: THREE.FlatShading } );
for ( var i = 0; i < 1; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
}
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setClearColor( scene.fog.color, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
render();
}
function animate() {
requestAnimationFrame( animate );
controls.update();
}
function render() {
renderer.render( scene, camera );
}
</script>
</body>
</html>
http://jsfiddle.net/alininja/b4qGx/1/
There are multiple options:
Use TubeGeometry - this is probably what you need
ExtrudeGeometry to extrude a disk
lathe an offset rectangle with LatheGeometry
Use THREE.Shape -> grab the tube like shape from the webgl_geometry_shapes sample
You can use the RingGeometry function. The following code adds to the scene a full (between 0 and 360 degrees) wireframed red ring of inner radius equals to 10 and outer radio equals to 20. You can play with the other indicated variables to adjust the aspect of the disc you want to generate ()
var geometry = new THREE.RingGeometry(10, 20, thetaSegments, phiSegments, 0, Math.PI * 2);
var ring = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({color: 0xff0000, wireframe: true}));
ring.position.set(25, 30, 0);
scene.add(ring);
Check this code!
var geometry = new THREE.TorusGeometry( 3, 0.5, 20, 2999 );
ring1 = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({color: 0xffffff, wireframe: true}));
scene.add(ring1);

Can't send a texture to a custom shader (ShaderPass/EffectComposer)

I'm currently playing with this great library, but I have some difficulties with the EffectComposer.
I can't send a texture to a postprocess shader introduced by a ShaderPass.
I think this is a bug... or I'm doing something stupid (needUpDate, warp,... ??)
(r54, W7, Nv9700mGT, FF 17.0.1 and Chrome 24.0.1312.52)
I used the "webgl_postprocessing.html" example to reproduce the phenomenon just by adding these shaders :
<script id="vertexShaderBasic" type="x-shader/x-vertex">
varying vec2 glTexCoord;
void main() {
glTexCoord = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShaderBlender" type="x-shader/x-fragment">
varying vec2 glTexCoord;
uniform sampler2D sprite1;
uniform sampler2D previousRender;
void main() {
vec3 color = texture2D( previousRender, glTexCoord ).rgb;
color += texture2D( sprite1, glTexCoord ).rgb;
gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;
}
</script>
this at the begining of the main script to be sure the sprite is loaded :
var composer2;
function start() {
init();
animate();
}
var sprite1 = THREE.ImageUtils.loadTexture( "textures/sprite1.jpg", null, start );
this in the composer field, after :
composer.addPass( new THREE.RenderPass( scene, camera ) );
composer2 = new THREE.EffectComposer( renderer );
var uniforms1 = {
sprite1: { type: "t", value: sprite1 }, // <- something wrong here
previousRender: { type: "t", value: null }
};
var blenderShader = {
uniforms: uniforms1,
vertexShader: $( 'vertexShaderBasic' ).textContent,
fragmentShader: $( 'fragmentShaderBlender' ).textContent
};
// link with the previous render
blenderShader.uniforms.previousRender.value = composer.renderTarget2;
// the custom shader
var blenderPass = new THREE.ShaderPass( blenderShader );
blenderPass.renderToScreen = true;
composer2.addPass( blenderPass );
I also coment this, beacause it's not relevent any more :
//effect.renderToScreen = true;
and I add this at the end :
composer2.render();
The link between the two passes work well, but the sprite never appear on the EffectComposer quad that cover the screen...
Thanks and sorry for my english.
EDIT to recap :
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - postprocessing</title>
<meta charset="utf-8">
<style>
body {
margin: 0px;
background-color: #000000;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.min.js"></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/shaders/DotScreenShader.js"></script>
<script src="js/shaders/RGBShiftShader.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script id="vertexShaderBasic" type="x-shader/x-vertex">
varying vec2 glTexCoord;
void main() {
glTexCoord = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="fragmentShaderBlender" type="x-shader/x-fragment">
varying vec2 glTexCoord;
uniform sampler2D sprite1;
uniform sampler2D previousRender;
void main() {
vec3 color = texture2D( previousRender, glTexCoord ).rgb;
color += texture2D( sprite1, glTexCoord ).rgb;
gl_FragColor.rgb = color;
gl_FragColor.a = 1.0;
}
</script>
<script>
var camera, scene, renderer, composer;
var composer2;
var object, light;
function start() {
init();
animate();
}
var sprite1 = THREE.ImageUtils.loadTexture( "textures/sprite1.jpg", null, start );
//var sprite1 = THREE.ImageUtils.loadTexture( "textures/sprite1.jpg", new THREE.UVMapping(), start ); // change anything
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
scene = new THREE.Scene();
scene.fog = new THREE.Fog( 0x000000, 1, 1000 );
object = new THREE.Object3D();
scene.add( object );
var geometry = new THREE.SphereGeometry( 1, 4, 4 );
var material = new THREE.MeshPhongMaterial( { color: 0xffffff, shading: THREE.FlatShading } );
for ( var i = 0; i < 100; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 ).normalize();
mesh.position.multiplyScalar( Math.random() * 400 );
mesh.rotation.set( Math.random() * 2, Math.random() * 2, Math.random() * 2 );
mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 50;
object.add( mesh );
}
scene.add( new THREE.AmbientLight( 0x222222 ) );
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
// postprocessing
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );
/////////////////////////////////////
/////////////////////////////////////
composer2 = new THREE.EffectComposer( renderer );
var uniforms1 = {
sprite1: { type: "t", value: sprite1 },
previousRender: { type: "t", value: null }
};
//uniforms1.sprite1.value.wrapS = uniforms1.sprite1.value.wrapT = THREE.RepeatWrapping;
var blenderShader = {
uniforms: uniforms1,
vertexShader: $( 'vertexShaderBasic' ).textContent,
fragmentShader: $( 'fragmentShaderBlender' ).textContent
};
blenderShader.uniforms.previousRender.value = composer.renderTarget2;
var blenderPass = new THREE.ShaderPass( blenderShader );
blenderPass.renderToScreen = true;
composer2.addPass( blenderPass );
/////////////////////////////////////
/////////////////////////////////////
var effect = new THREE.ShaderPass( THREE.DotScreenShader );
effect.uniforms[ 'scale' ].value = 4;
composer.addPass( effect );
var effect = new THREE.ShaderPass( THREE.RGBShiftShader );
effect.uniforms[ 'amount' ].value = 0.0015;
//effect.renderToScreen = true;
composer.addPass( effect );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function $( id ) {
return document.getElementById( id );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
var time = Date.now();
object.rotation.x += 0.005;
object.rotation.y += 0.01;
composer.render();
composer2.render();
}
</script>
</body>
</html>
What I want :
Good
What I get :
NotGood
I had this issue too, and found a workaround.
I debugged it to determine that the texture ID for the extra texture is different in the shader pass than expected, which is bad. If you look in the ShaderPass constructor, you'll see it clones the uniforms. That seems to be the cause. If I edit that code to restore the original texture object in the cloned uniforms, it works as expected. So that should work for you too.
I'm trying to get some kind of (proper) bug fix integrated into the actual release.
Try this
var sprite1 = THREE.ImageUtils.loadTexture( "textures/sprite1.jpg", new THREE.UVMapping(), start );
three.js r.54

Resources