Three JS rendering from one buffer into itself - javascript

I really hope you can help me with this question, as it confusses me since some time:
I have a three js context.
There i create a custom material and let it render into a texture.
`
/* Texture render environment */
fbo_renderer_scene = new THREE.Scene();
fbo_renderer_ripple_update_scene = new THREE.Scene();
var fbo_texture_light = new THREE.DirectionalLight(0xFFFFFF, 1.5);
fbo_texture_light.position.set(0.0, 0.0, -1.0).normalize();
fbo_renderer_scene.add(fbo_texture_light);
fbo_renderer_ripple_update_scene.add(fbo_texture_light);
ripple_texture = new THREE.WebGLRenderTarget(width, height, render_target_params);
ripple_update_texture = new THREE.WebGLRenderTarget(width, height, render_target_params);
ripple_material = new THREE.ShaderMaterial(
{
uniforms:
{
texture1: { type: "t", value: bottom_plane_texture},
},
vertexShader: document.getElementById('drop_vert_shader').textContent,
fragmentShader: document.getElementById('drop_frag_shader').textContent,
depthWrite: false
});
fbo_renderer_camera = new THREE.OrthographicCamera(width / -2.0, width / 2.0, height / 2.0, height / -2.0, -10000, 10000);
var texture_mesh = new THREE.Mesh(bottom_plane_geometry, ripple_material);
fbo_renderer_scene.add(texture_mesh);
renderer.render(fbo_renderer_scene, fbo_renderer_camera, ripple_texture, true);
Ok fine now i have everything in ripple_texture
Now there is another shader which should update the ripple shape on Animate() gets called.
And of course it should render the result into the ripple_texture again:
/* Texture render environment */
fbo_renderer_scene = new THREE.Scene();
fbo_renderer_ripple_update_scene = new THREE.Scene();
var fbo_texture_light = new THREE.DirectionalLight(0xFFFFFF, 1.5);
fbo_texture_light.position.set(0.0, 0.0, -1.0).normalize();
fbo_renderer_scene.add(fbo_texture_light);
fbo_renderer_ripple_update_scene.add(fbo_texture_light);
ripple_texture = new THREE.WebGLRenderTarget(width, height, render_target_params);
ripple_update_texture = new THREE.WebGLRenderTarget(width, height, render_target_params);
ripple_material = new THREE.ShaderMaterial(
{
uniforms:
{
texture1: { type: "t", value: bottom_plane_texture},
},
vertexShader: document.getElementById('drop_vert_shader').textContent,
fragmentShader: document.getElementById('drop_frag_shader').textContent,
depthWrite: false
});
fbo_renderer_camera = new THREE.OrthographicCamera(width / -2.0, width / 2.0, height / 2.0, height / -2.0, -10000, 10000);
var texture_mesh = new THREE.Mesh(bottom_plane_geometry, ripple_material);
fbo_renderer_scene.add(texture_mesh);
renderer.render(fbo_renderer_scene, fbo_renderer_camera, ripple_texture, true);
But everytime i try to do this Chromium reports this:
WebGLRenderingContext-0x3d022469aa80]GL ERROR :GL_INVALID_OPERATION :
glDrawElements: Source and destination textures of the draw are the
same.
Firefox just shows a black canvas.
I guess this might be a timing issue but im not sure and have no clue how to bypass this, since three js has no callback for render.

It's telling you what's wrong:
WebGLRenderingContext-0x3d022469aa80]GL ERROR :GL_INVALID_OPERATION : glDrawElements: Source and destination textures of the draw are the same.
You can't read and render to the same texture at once because your render operations will trample the texture data you're reading from during rendering, giving you garbage output.
You'll need to have another texture that you render into that isn't the same as the one you're reading from.

Related

ThreeJS WebGLRenderTarget has blank texture

Using A-Frame and Three.JS, I want to render to a WebGLRenderTarget and create a material based on its texture, like so:
var targetPlane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(2, 2),
new THREE.MeshBasicMaterial({color: 'blue'})
);
targetPlane.position.set(-2, 1, -2);
scene.add(targetPlane);
var redPlane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(2, 2),
new THREE.MeshBasicMaterial({color: 'red'})
);
redPlane.position.set(2, 1, -2);
scene.add(redPlane);
var myCamera = new THREE.PerspectiveCamera(45, 1, 0.1, 1000);
myCamera.position.set(2, 1, 0);
scene.add(myCamera);
var myRenderer = new THREE.WebGLRenderer();
myRenderer.setSize(200, 200);
myRenderer.render(scene, myCamera);
var renderTarget = new THREE.WebGLRenderTarget(200, 200, {minFilter: THREE.LinearFilter,
magFilter: THREE.NearestFilter});
myRenderer.setRenderTarget(renderTarget);
myRenderer.render(scene, myCamera);
targetPlane.material = new THREE.MeshBasicMaterial({map: renderTarget.texture});
renderer.render(scene, camera);
The outcome is just a blank white material. The camera settings are correct. The following code works:
myRenderer.setRenderTarget(null);
myRenderer.render(scene, myCamera);
img.src = myRenderer.domElement.toDataURL();
I made a fiddle to demonstrate.
You can't use a render target produced by a WebGLRenderer with a different instance of WebGLRenderer because WebGL resources like buffers, textures or shader programs can't be shared across WebGL contexts.
Hence, you have to use renderer to produce the render target and to render the final scene.
Updated fiddle: https://jsfiddle.net/x4snr9tq/
three.js R112.

Why is my canvas element showing only a black screen?

I'm setting up a 3d asset viewer in Three.js. I'm running the code on a Plesk server provided by the university and have it linked via Dreamweaver. I'm a total newbie to JS and it was suggested in many threads and posts that I wrap my code within an 'init();' function. Up doing so, and clearing any errors that the code had, it is now showing a black screen, rather than the 3d model it would show before.
I've spent the whole day error checking removing problems that I was having which included the 'canvas' not being created inside the 'container' div, and the 'onWindowResize' function. All these problems have been resolved, and there are no errors in the code apparently. I've got ambient lights in the code and there was a working skybox, so I'm sure its not a problem with position of camera or lack of lighting.
I know that you need as little code as possible, but I have no idea where the problem is coming from, so a majority of the code on the page is here :
<div id="container" ></div>
<script>
let container;
let camera;
let controls;
let scene;
let renderer;
init();
animate;
function init(){
// Renderer - WebGL is primary Renderer for Three.JS
var renderer = new THREE.WebGLRenderer({antialias : true});
renderer.setClearColor(0xEEEEEE, 0.5);
// Selects and applies parameters to the 'Container' div
var container = document.querySelector("#container");
container.appendChild(renderer.domElement);
renderer.setSize(container.clientWidth, container.clientHeight);
// Perspective Camera (FOV, aspect ratio based on container, near, far)
var camera = new THREE.PerspectiveCamera( 75, container.clientWidth / container.clientHeight, 0.1, 1000);
camera.position.x = 750;
camera.position.y = 500;
camera.position.z = 1250;
// Scene will contain all objects in the world
var scene = new THREE.Scene();
//Lighting (Colour, intensity)
var light1Ambient = new THREE.AmbientLight(0xffffff , 0.3);
scene.add(light1Ambient);
var light1Point = new THREE.PointLight(0xfff2c1, 0.5, 0, 2);
scene.add(light1Point);
var light2Point = new THREE.PointLight(0xd6e3ff, 0.4, 0, 2);
scene.add(light2Point);
// All basic Geomety
var newPlane = new THREE.PlaneGeometry(250,250,100,100);
const mesh = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshBasicMaterial( {color: 0x00ff00} )
);
scene.add(mesh);
// Water
water = new THREE.Water(newPlane,
{
textureWidth: 512,
textureHeight: 512,
waterNormals: new THREE.TextureLoader().load( 'http://up826703.ct.port.ac.uk/CTPRO/textures/waternormals.jpg', function ( texture ) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
} ),
alpha: 1.0,
sunDirection: light1Point.position.clone().normalize(),
sunColor: 0xffffff,
waterColor: 0x001e0f,
distortionScale: 0.5,
fog: scene.fog !== undefined
}
);
water.rotation.x = - Math.PI / 2;
scene.add( water );
// All Materials (Normal for Debugging) (Lambert: color)
var material = new THREE.MeshLambertMaterial({color: 0xF3FFE2});
var materialNew = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
// Skybox
var skybox = new THREE.BoxGeometry(1000,1000, 1000);
var skyboxMaterials =
[
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_ft.jpg"), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_bk.jpg"), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_up.jpg"), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_dn.jpg"), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_rt.jpg"), side: THREE.DoubleSide }),
new THREE.MeshBasicMaterial({map: new THREE.TextureLoader().load("http://up826703.ct.port.ac.uk/CTPRO/skybox/blue/bluecloud_lf.jpg"), side: THREE.DoubleSide }),
];
var skyboxMaterial = new THREE.MeshFaceMaterial(skyboxMaterials);
var skyMesh = new THREE.Mesh (skybox, skyboxMaterial);
scene.add(skyMesh);
//Grid Helper Beneath Ship
scene.add(new THREE.GridHelper(250, 250));
//OBJ Model Loading
var objLoader = new THREE.OBJLoader();
objLoader.load('http://up826703.ct.port.ac.uk/CTPRO/models/ship1.obj', function(object){
scene.add(object);
});
// Object positioning
water.position.y = -2.5;
// Misc Positioning
light1Point.position.z =20;
light1Point.position.x = 25;
// z - front-back position
light2Point.position.z = -400;
// x - left-right
light2Point.position.x = -25;
// y - up- down
light2Point.position.y = 250;
window.addEventListener("resize", onWindowResize, false);
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
};
};
// Canvas adapts size based on changing windows size
//Render loop
var animate = function(){
water.material.uniforms[ "time" ].value += 1.0 / 120.0;
function drawFrame(ts){
var center = new THREE.Vector2(0,0);
window.requestAnimationFrame(drawFrame);
var vLength = newPlane.geometry.vertices.length;
for (var i = 0; i < vLength; i++) {
var v = newPlane.geometry.vertices[i];
var dist = new THREE.Vector2(v.x, v.y).sub(center);
var size = 2.0;
var magnitude = 8;
v.z = Math.sin(dist.length()/-size + (ts/900)) * magnitude;
}
newPlane.geometry.verticesNeedUpdate = true;
};
requestAnimationFrame(animate)
renderer.render(scene, camera);
controls.update();
}
</script>
I'm no professional, so I'm sorry if this is super rough for those of you with experience!
I need to point out, before wrapping all of this in the init(); function, it was working perfectly.
When working, I should see a crudely modeled ship sitting in some water, with a cloud skybox. The controls were working and it would auto rotate.
Right now it does none of this. The obj loader is working as seen in the chrome console log OBJLoader: 1661.970703125ms but again, nothing is actually displayed, it's just a black screen.
Thanks to anyone who's able to help me out with this!
this line
animate;
needs to a function call
animate();
Also you probably need to change the code below where you create the animate function from
var animate = function(){
To this
function animate(){
The reason is named functions are defined when the code is loaded but variables var are created when the code is executed. So with code like this
init();
animate();
var animate = function(){ ...
animate doesn't actually exist at the point the code tries to call it whereas with this
init();
animate();
function animate(){ ...
it does exist
You could also re-arrange the code so for example define animate before you use it should work.
var animate = function(){
...
};
init();
animate();
It also appear some are declared inside init which means that are not available to animate. So for example
var renderer = new THREE.WebGLRenderer({antialias : true});
declares a new variable renderer that only init can see. You wanted to set the renderer variable that is outside of init so change the code to
renderer = new THREE.WebGLRenderer({antialias : true});
controls is never defined so you probably need to define it or comment out
controls.update();
to
// controls.update();
note: you might find these tutorials helpful although if you're new to JavaScript you should probably spend time learning JavaScript

Uncaught Error: THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order

I am using ThreeJS/WebGL to demonstrate a rotating planet inside of an iframe. It is working fine except for a warning and an issue:
Warning: THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.
Issue: Uncaught Error: THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.
I have included the following assets into the body of the iframe:
<div id="webgl"></div>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.js"></script>
<script type="text/javascript" src="https://cdn.rawgit.com/bubblin/The-Solar-System/master/jupiter/js/detector.js">
</script>
<script type="text/javascript" src="https://cdn.rawgit.com/bubblin/The-Solar-System/master/jupiter/js/trackBallControls.js"> </script>
<script type="text/javascript" src="https://cdn.rawgit.com/bubblin/The-Solar-System/master/jupiter/js/xRingGeometry.js"> </script>
As you can see I'm using trackBallControls.js to rotate the sphere based on user input/touch and I have overridden a RingGeometry method to layer the ring_image along the circumference instead of emanating it radially.
Here's my complete code to simulate Jupiter, for example:
(function() {
var webglEl = document.getElementById('webgl');
if (!Detector.webgl) {
Detector.addGetWebGLMessage(webglEl);
return;
}
THREE.ImageUtils.crossOrigin = '';
var width = window.innerWidth,
height = window.innerHeight;
var radius = 0.45,
segments = 32,
rotation = 5;
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(45, width / height, 0.01, 1000);
camera.position.z = 3;
camera.position.y = 1;
camera.position.x = -1;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
scene.add(new THREE.AmbientLight(0x553333));
var light = new THREE.DirectionalLight(0xffffff, .5);
light.position.set(5, 3, 5);
scene.add(light);
var sphere = createSphere(radius, segments);
sphere.rotation.y = rotation;
scene.add(sphere);
var rings = createRings(radius, segments);
rings.rotation.y = rotation;
scene.add(rings);
var stars = createStars(90, 64);
scene.add(stars);
var controls = new THREE.TrackballControls(camera);
webglEl.appendChild(renderer.domElement);
render();
function render() {
controls.update();
sphere.rotation.y += 0.08;
rings.rotation.y += 0.05;
requestAnimationFrame(render);
renderer.render(scene, camera);
}
function createSphere(radius, segments) {
return new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshPhongMaterial({
map: THREE.ImageUtils.loadTexture('https://cdn.rawgit.com/marvindanig/Images/master/jupiter/jupitermap.jpg'),
bumpScale: 0.05,
specular: new THREE.Color('#190909')
}));
}
function createRings(radius, segments) {
return new THREE.Mesh(new THREE.XRingGeometry(1.3 * radius, 1.6 * radius, 2 * segments, 5, 0, Math.PI * 2), new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('https://cdn.rawgit.com/bubblin/The-Solar-System/master/jupiter/images/rings.png'),
side: THREE.DoubleSide,
transparent: true,
opacity: 0.2,
specular: new THREE.Color('#495909')
}));
}
function createStars(radius, segments) {
return new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('https://cdn.rawgit.com/marvindanig/Images/master/galaxy_starfield.png'),
side: THREE.BackSide
}));
}
}());
It works (See the output here), but I get those warnings and an error. The errors and warnings eventually build up, and my app crashes on the iPad/iPhone.
Please help, I am new to ThreeJS.
Regarding:
Warning: THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter should be set to THREE.NearestFilter or THREE.LinearFilter.
In computer graphics, textures that are powers of two can be processed faster than those that are not which is why you get the warning. I suggest you resize both the sphere texture and rings texture to square powers of two using your favourite image editor. Alternatively, you can set the minFilter as suggested by the warning:
function createSphere(radius, segments) {
var texture = THREE.ImageUtils.loadTexture('https://cdn.rawgit.com/marvindanig/Images/master/jupiter/jupitermap.jpg');
texture.minFilter = THREE.NearestFilter;
return new THREE.Mesh(new THREE.SphereGeometry(radius, segments, segments), new THREE.MeshPhongMaterial({
map: texture,
bumpScale: 0.05,
specular: new THREE.Color('#190909')
}));
}
As for the second error, I'm not seeing it on Chrome 42 but it is caused because one of your scripts (that likely isn't shown here as I could not find the problem in any of them) uses Quaternion.setFromEuler like this:
quat.setFromEuler(new THREE.Vector3(pitch, yaw, roll));
when it should it be using like this:
quat.setFromEuler(new THREE.Euler(pitch, yaw, roll));
Do a quick CTRL + F in your code and you'll find the faulty script.

Keep light fixed when using THREE.ShaderTerrain in three.js

I have a sphere and light source (basically sun and earth). On the sphere, I'm using a greyscale heightmap for terrain texture so I am using three.js's ShaderTerrain.js. I'm also using a directional light source. My code:
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
lightCamera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
controls = new THREE.OrbitControls( camera, renderer.domElement );
lightControls = new THREE.TrackballControls(lightCamera, renderer.domElement);
light = new THREE.DirectionalLight(0xffffff, 1.5);
light.position.set(0,0,20);
lightCamera.position.z = 225;
lightCamera.add(light);
camera.add(lightCamera);
scene.add(camera);
var terrainShader = THREE.ShaderTerrain[ "terrain" ];
uniformsTerrain = THREE.UniformsUtils.clone(terrainShader.uniforms);
// displacement is heightmap (greyscale image)
uniformsTerrain[ "tDisplacement" ].value = THREE.ImageUtils.loadTexture('heightmap.jpg');
uniformsTerrain[ "uDisplacementScale" ].value = 15;
// diffuse is texture overlay
uniformsTerrain[ "tDiffuse1" ].value = THREE.ImageUtils.loadTexture('earth.jpg');
uniformsTerrain[ "enableDiffuse1" ].value = true;
var material = new THREE.ShaderMaterial({
uniforms: uniformsTerrain,
vertexShader: terrainShader.vertexShader,
fragmentShader: terrainShader.fragmentShader,
lights: true,
fog: false
});
var geometry = new THREE.SphereGeometry(100,100,100);
geometry.computeTangents();
scene.add(new THREE.Mesh(geometry, material));
With this code, the sphere is created just fine with the raised texture and everything.
The problem is that when I rotate the sphere, the light source doesn't appear to stay fixed. It rotates with the sphere and you end up with dark spots (rather than the light always coming from the front and keeping what the users sees illuminated).
If I create a simple sphere instead, like so:
geometry = new THREE.SphereGeometry(100,100,100);
material = new THREE.MeshLambertMaterial({color: 0x00ee00, wireframe: true, transparent: true, needsUpdate: true});
sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
Everything works perfectly. The light source stays fixed while the sphere/camera rotates.
I've also tried code that simply rotates the (first) sphere (the one using the ShaderMaterial) and not the camera (attaching a function to the mousemove event and simply doing a sphere.rotation.x/y with the mouse position). This doesn't work either; When the sphere rotates, there are still shadows that appear to the user.
Not sure what I'm missing here.
Here's a jsfiddle: http://jsfiddle.net/Z5sS5/1/. Left click/drag to spin everything (camera + light), right click/drag to spin only the light. To see it working, keep basicSphere() uncommented. To see it not working, comment basicSphere() and uncomment terrainSphere().

Three.js light is not static

I need to create static light (invariant to move of camera) and i need to get actual position of light in fragment shader.
What i am doing now :
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(60, canvas.width() / canvas.height(), 1, 10000);
camera.position.z = 2000;
camera.lookAt(0, 0, 0);
var light = new THREE.SpotLight(0xFFFFFF, 1);
light.position.set(0.5, 0.5, 0.1).normalize();
camera.add(light);
....
var lambertShader = THREE.ShaderLib['lambert'];
uniformsVolume = THREE.UniformsUtils.clone(lambertShader.uniforms);
....
materialVolumeRendering = new THREE.ShaderMaterial({
uniforms: uniformsVolume,
vertexColors: THREE.VertexColors,
vertexShader: vertVolumeRendering,
fragmentShader: fragVolumeRendering,
vertexColors: THREE.VertexColors,
lights :true
});
....
scene.add(camera);
Than in fragment shader i set uniform variable:
uniform vec3 spotLightPosition;
and compute light for voxel:
float dProd = max(0.0, dot(getGradient(posInCube), normalize(lightPos
- posInCube)));
voxelColored.rgb = voxelColored.rgb * dProd + voxelColored.rgb * 0.2;
Problem is, that it doesnt work correctly. My idea is, that i will moving with object (in reality with camera). Light will shine still from the same side (will be static in scene). At this time light is not static and work very strange.
Any idea?
Somebody please...
Tanks a lot.
Tomáš
Try with PointLight instead. SpotLight is a bit trickier to use.
To make your light static in position, then don't add it to your camera, but instead add it to the scene:
scene.add(light);
To find the position reference the variable you used for the light.
light.position

Categories

Resources