Browse Source

add render target article

Gregg Tavares 6 years ago
parent
commit
db5eb156ba

+ 2 - 3
threejs/lessons/threejs-picking.md

@@ -257,8 +257,7 @@ This can solve issue 2 and 3 above. As for issue 1, speed, it really depends. Ev
 
 One thing we can do though is since we're only going to be reading one pixel we can just setup the camera so only that one pixel is drawn. We can do this using `PerspectiveCamera.setViewOffset` which lets us tell THREE.js to compute a camera that just renders a smaller part of a larger rectangle. This should save some time.
 
-To do this type of picking in THREE.js at the moment requires we create 2 scenes. One we will fill with our normal meshes. The other we'll fill with meshes
-that use our picking material.
+To do this type of picking in THREE.js at the moment requires we create 2 scenes. One we will fill with our normal meshes. The other we'll fill with meshes that use our picking material.
 
 So, first create a second scene and make sure it clears to black.
 
@@ -325,7 +324,7 @@ function setPickPosition(event) {
 }
 ```
 
-Then let's change the `PickHelper` into a `GPUPickHelper`
+Then let's change the `PickHelper` into a `GPUPickHelper`. It will use a `WebGLRenderTarget` like we covered the [article on render targets](threejs-rendertargets.html). Our render target here is only a single pixel in size, 1x1. 
 
 ```
 -class PickHelper {

+ 4 - 0
threejs/lessons/threejs-post-processing.md

@@ -0,0 +1,4 @@
+Title: Three.js Post Processing
+Description: How to Post Process in THREE.js
+
+TBD

+ 145 - 0
threejs/lessons/threejs-rendertargets.md

@@ -0,0 +1,145 @@
+Title: Three.js Render Targets
+Description: How to render to a texture.
+
+A render target in three.js is basicaly a texture you can render to.
+After you render to it you can use that texture like any other texture.
+
+Let's make a simple example. We'll start with an example from [the article on responsiveness](threejs-responsive.html).
+
+Rendering to a render target just almost exactly the same as normal rendering. First we create a `WebGLRenderTarget`.
+
+```
+const rtWidth = 512;
+const rtHeight = 512;
+const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);
+```
+
+Then we need a `Camera` and a `Scene`
+
+```
+const rtFov = 75;
+const rtAspect = rtWidth / rtHeight;
+const rtNear = 0.1;
+const rtFar = 5;
+const rtCamera = new THREE.PerspectiveCamera(rtFov, rtAspect, rtNear, rtFar);
+rtCamera.position.z = 2;
+
+const rtScene = new THREE.Scene();
+rtScene.background = new THREE.Color('red');
+```
+
+Notice we set the aspect to the aspect for the render target, not the canvas.
+
+We fill the scene with stuff. In this case we're using the light and the 3 cubes [from the previous article](threejs-responsive.html).
+
+```
+{
+  const color = 0xFFFFFF;
+  const intensity = 1;
+  const light = new THREE.DirectionalLight(color, intensity);
+  light.position.set(-1, 2, 4);
+*  rtScene.add(light);
+}
+
+const boxWidth = 1;
+const boxHeight = 1;
+const boxDepth = 1;
+const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+
+function makeInstance(geometry, color, x) {
+  const material = new THREE.MeshPhongMaterial({color});
+
+  const cube = new THREE.Mesh(geometry, material);
+*  rtScene.add(cube);
+
+  cube.position.x = x;
+
+  return cube;
+}
+
+*const rtCubes = [
+  makeInstance(geometry, 0x44aa88,  0),
+  makeInstance(geometry, 0x8844aa, -2),
+  makeInstance(geometry, 0xaa8844,  2),
+];
+```
+
+The `Scene` and `Camera` from previous article are still there. We'll use them to render to the canvas.
+We just need to add stuff to render.
+
+Let's add a cube that uses the render target's texture.
+
+```
+const material = new THREE.MeshPhongMaterial({
+  map: renderTarget.texture,
+});
+const cube = new THREE.Mesh(geometry, material);
+scene.add(cube);
+```
+
+Now at render time first we render the render target scene to the render target.
+
+```
+function render(time) {
+  time *= 0.001;
+
+  ...
+
+  // rotate all the cubes in the render target scene
+  rtCubes.forEach((cube, ndx) => {
+    const speed = 1 + ndx * .1;
+    const rot = time * speed;
+    cube.rotation.x = rot;
+    cube.rotation.y = rot;
+  });
+
+  // draw render target scene to render target
+  renderer.render(rtScene, rtCamera, renderTarget);
+
+```
+
+Then we render the scene with the single cube that is using the render target's texture to the canvas.
+
+```
+  // rotate the cube in the scene
+  cube.rotation.x = time;
+  cube.rotation.y = time * 1.1;
+
+  // render the scene to the canvas
+  renderer.render(scene, camera);
+```
+
+And voilà
+
+{{{example url="../threejs-render-target.html" }}}
+
+The cube is red because we set the `background` of the `rtScene` to red so the render target's texture is being cleared to red.
+
+Render target are used for all kinds of things. Shadows use a render target. [Picking can use a render target](threejs-picking.html). Various kinds of [post processing effects](threejs-post-processing.html) require a render target.
+
+A few notes about using `WebGLRenderTarget`.
+
+* By default `WebGLRenderTarget` creates 2 textures. A color texture and a depth/stencil texture. If you don't need the depth or stencil textures you can request it not create them by passing in options. Example:
+
+        const rt = new THREE.WebGLRenderTarget(width, height, {
+          depthBuffer: false,
+          stencilBuffer: false,
+        });
+
+* You might need to change the size of a render target
+
+  In the example above we make a render target of a fixed size, 512x512. For things like post processing you generally need to make a render target the same size as your canvas. In our code that would mean when we change the canvas size we would also update both the render target size and the camera we're using when rendering to the render target. Example:
+
+        function render(time) {
+          time *= 0.001;
+      
+          if (resizeRendererToDisplaySize(renderer)) {
+            const canvas = renderer.domElement;
+            camera.aspect = canvas.clientWidth / canvas.clientHeight;
+            camera.updateProjectionMatrix();
+
+        +    renderTaret.setSize(canvas.width, canvas.height);
+        +    rtCamera.aspect = camera.aspect;
+        +    rtCamera.updateProjectionMatrix();
+          }
+

+ 1 - 0
threejs/lessons/toc.html

@@ -27,6 +27,7 @@
     <li><a href="/threejs/lessons/threejs-cameras.html">Cameras</a></li>
     <li><a href="/threejs/lessons/threejs-shadows.html">Shadows</a></li>
     <li><a href="/threejs/lessons/threejs-fog.html">Fog</a></li>
+    <li><a href="/threejs/lessons/threejs-rendertargets.html">Render Targets</a></li>
   </ul>
   <li>Reference</li>
   <ul>

+ 146 - 0
threejs/threejs-render-target.html

@@ -0,0 +1,146 @@
+<!-- Licensed under a BSD license. See license.html for license -->
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
+    <title>Three.js - Responsive</title>
+    <style>
+    body {
+        margin: 0;
+    }
+    #c {
+        width: 100vw;
+        height: 100vh;
+        display: block;
+    }
+    </style>
+  </head>
+  <body>
+    <canvas id="c"></canvas>
+  </body>
+<script src="resources/threejs/r98/three.min.js"></script>
+<script>
+'use strict';
+
+/* global THREE */
+
+function main() {
+  const canvas = document.querySelector('#c');
+  const renderer = new THREE.WebGLRenderer({canvas: canvas});
+
+  const rtWidth = 512;
+  const rtHeight = 512;
+  const renderTarget = new THREE.WebGLRenderTarget(rtWidth, rtHeight);
+
+  const rtFov = 75;
+  const rtAspect = rtWidth / rtHeight;
+  const rtNear = 0.1;
+  const rtFar = 5;
+  const rtCamera = new THREE.PerspectiveCamera(rtFov, rtAspect, rtNear, rtFar);
+  rtCamera.position.z = 2;
+
+  const rtScene = new THREE.Scene();
+  rtScene.background = new THREE.Color('red');
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    rtScene.add(light);
+  }
+
+  const boxWidth = 1;
+  const boxHeight = 1;
+  const boxDepth = 1;
+  const geometry = new THREE.BoxGeometry(boxWidth, boxHeight, boxDepth);
+
+  function makeInstance(geometry, color, x) {
+    const material = new THREE.MeshPhongMaterial({color});
+
+    const cube = new THREE.Mesh(geometry, material);
+    rtScene.add(cube);
+
+    cube.position.x = x;
+
+    return cube;
+  }
+
+  const rtCubes = [
+    makeInstance(geometry, 0x44aa88,  0),
+    makeInstance(geometry, 0x8844aa, -2),
+    makeInstance(geometry, 0xaa8844,  2),
+  ];
+
+  const fov = 75;
+  const aspect = 2;  // the canvas default
+  const near = 0.1;
+  const far = 5;
+  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
+  camera.position.z = 2;
+
+  const scene = new THREE.Scene();
+
+  {
+    const color = 0xFFFFFF;
+    const intensity = 1;
+    const light = new THREE.DirectionalLight(color, intensity);
+    light.position.set(-1, 2, 4);
+    scene.add(light);
+  }
+
+  const material = new THREE.MeshPhongMaterial({
+    map: renderTarget.texture,
+  });
+  const cube = new THREE.Mesh(geometry, material);
+  scene.add(cube);
+
+  function resizeRendererToDisplaySize(renderer) {
+    const canvas = renderer.domElement;
+    const width = canvas.clientWidth;
+    const height = canvas.clientHeight;
+    const needResize = canvas.width !== width || canvas.height !== height;
+    if (needResize) {
+      renderer.setSize(width, height, false);
+    }
+    return needResize;
+  }
+
+  function render(time) {
+    time *= 0.001;
+
+    if (resizeRendererToDisplaySize(renderer)) {
+      const canvas = renderer.domElement;
+      camera.aspect = canvas.clientWidth / canvas.clientHeight;
+      camera.updateProjectionMatrix();
+    }
+
+    // rotate all the cubes in the render target scene
+    rtCubes.forEach((cube, ndx) => {
+      const speed = 1 + ndx * .1;
+      const rot = time * speed;
+      cube.rotation.x = rot;
+      cube.rotation.y = rot;
+    });
+
+    // draw render target scene to render target
+    renderer.render(rtScene, rtCamera, renderTarget);
+
+    // rotate the cube in the scene
+    cube.rotation.x = time;
+    cube.rotation.y = time * 1.1;
+
+    // render the scene to the canvas
+    renderer.render(scene, camera);
+
+    requestAnimationFrame(render);
+  }
+
+  requestAnimationFrame(render);
+}
+
+main();
+</script>
+</html>
+