Skip to content

Commit a3c4192

Browse files
sergey-tunaMugen87
andauthored
WebGLRenderer: Fix setRenderTarget()'s activeMipmapLevel (mrdoob#26347)
* Ability to set the mip levels of a texture as a render target * Render to texture's mip levels example * Render to texture's mip levels example: added a screenshot, fixed errors * Render to texture's mip levels example: added the example to json files * Render to texture's mip levels example: updated the screenshot * Update webgl_materials_cubemap_render_to_mipmaps.html Clean up. * Update WebGLRenderer.js Code style. * Update WebGLRenderer.js Code style. * Update WebGLRenderer.js Clean up. --------- Co-authored-by: Michael Herzog <michael.herzog@human-interactive.org>
1 parent 9abfda6 commit a3c4192

File tree

6 files changed

+385
-14
lines changed

6 files changed

+385
-14
lines changed

examples/files.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@
142142
"webgl_materials_cubemap_dynamic",
143143
"webgl_materials_cubemap_refraction",
144144
"webgl_materials_cubemap_mipmaps",
145+
"webgl_materials_cubemap_render_to_mipmaps",
145146
"webgl_materials_curvature",
146147
"webgl_materials_displacementmap",
147148
"webgl_materials_envmaps",

examples/tags.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"webgl_materials_blending_custom": [ "alpha" ],
4141
"webgl_materials_channels": [ "normal", "depth", "rgba packing" ],
4242
"webgl_materials_cubemap_mipmaps": [ "envmap" ],
43+
"webgl_materials_cubemap_render_to_mipmaps": [ "envmap", "renderTarget", "mipmap" ],
4344
"webgl_materials_envmaps_hdr": [ "rgbm" ],
4445
"webgl_materials_lightmap": [ "shadow" ],
4546
"webgl_materials_physical_clearcoat": [ "anisotropy" ],
Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,278 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<title>three.js webgl - materials - cubemap mipmaps</title>
5+
<meta charset="utf-8">
6+
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
7+
<link type="text/css" rel="stylesheet" href="main.css">
8+
</head>
9+
<body>
10+
11+
<div id="container"></div>
12+
<div id="info">
13+
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - rendering to cubemap mip levels demo.<br/>
14+
Left: original cubemap<br/>
15+
Right: generated cubemap<br/>
16+
</div>
17+
18+
<!-- Import maps polyfill -->
19+
<!-- Remove this when import maps will be widely supported -->
20+
<script async src="https://unpkg.com/es-module-shims@1.6.3/dist/es-module-shims.js"></script>
21+
22+
<script type="importmap">
23+
{
24+
"imports": {
25+
"three": "../build/three.module.js",
26+
"three/addons/": "./jsm/"
27+
}
28+
}
29+
</script>
30+
31+
<script type="module">
32+
33+
import * as THREE from 'three';
34+
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
35+
36+
let container;
37+
let camera, scene, renderer;
38+
39+
const CubemapFilterShader = {
40+
uniforms: {
41+
cubeTexture: { value: null },
42+
mipIndex: { value: 0 },
43+
},
44+
45+
vertexShader: /* glsl */ `
46+
47+
varying vec3 vWorldDirection;
48+
49+
#include <common>
50+
51+
void main() {
52+
vWorldDirection = transformDirection(position, modelMatrix);
53+
#include <begin_vertex>
54+
#include <project_vertex>
55+
gl_Position.z = gl_Position.w; // set z to camera.far
56+
}
57+
58+
`,
59+
60+
fragmentShader: /* glsl */ `
61+
62+
uniform samplerCube cubeTexture;
63+
varying vec3 vWorldDirection;
64+
65+
uniform float mipIndex;
66+
67+
#include <common>
68+
69+
void main() {
70+
vec3 cubeCoordinates = normalize(vWorldDirection);
71+
72+
// Colorize mip levels
73+
vec4 color = vec4(1.0, 0.0, 0.0, 1.0);
74+
if (mipIndex == 0.0) color.rgb = vec3(1.0, 1.0, 1.0);
75+
else if (mipIndex == 1.0) color.rgb = vec3(0.0, 0.0, 1.0);
76+
else if (mipIndex == 2.0) color.rgb = vec3(0.0, 1.0, 1.0);
77+
else if (mipIndex == 3.0) color.rgb = vec3(0.0, 1.0, 0.0);
78+
else if (mipIndex == 4.0) color.rgb = vec3(1.0, 1.0, 0.0);
79+
80+
gl_FragColor = textureCube(cubeTexture, cubeCoordinates, 0.0) * color;
81+
}
82+
83+
`,
84+
};
85+
86+
87+
init();
88+
animate();
89+
90+
91+
async function loadCubeTexture( urls ) {
92+
93+
return new Promise( function ( resolve ) {
94+
95+
new THREE.CubeTextureLoader().load( urls, function ( cubeTexture ) {
96+
97+
resolve( cubeTexture );
98+
99+
} );
100+
101+
102+
} );
103+
104+
}
105+
106+
function allocateCubemapRenderTarget( cubeMapSize ) {
107+
108+
const params = {
109+
magFilter: THREE.LinearFilter,
110+
minFilter: THREE.LinearMipMapLinearFilter,
111+
generateMipmaps: false,
112+
type: THREE.HalfFloatType,
113+
format: THREE.RGBAFormat,
114+
colorSpace: THREE.LinearSRGBColorSpace,
115+
depthBuffer: false,
116+
};
117+
118+
const rt = new THREE.WebGLCubeRenderTarget( cubeMapSize, params );
119+
120+
const mipLevels = Math.log( cubeMapSize ) * Math.LOG2E + 1.0;
121+
for ( let i = 0; i < mipLevels; i ++ ) rt.texture.mipmaps.push( {} );
122+
123+
rt.texture.mapping = THREE.CubeReflectionMapping;
124+
return rt;
125+
126+
}
127+
128+
function renderToCubeTexture( cubeMapRenderTarget, sourceCubeTexture ) {
129+
130+
const cameras = [];
131+
132+
for ( let i = 0; i < 6; i ++ ) {
133+
134+
// negative fov is not an error
135+
cameras.push( new THREE.PerspectiveCamera( - 90, 1, 1, 10 ) );
136+
137+
}
138+
139+
cameras[ 0 ].up.set( 0, 1, 0 );
140+
cameras[ 0 ].lookAt( 1, 0, 0 );
141+
cameras[ 1 ].up.set( 0, 1, 0 );
142+
cameras[ 1 ].lookAt( - 1, 0, 0 );
143+
cameras[ 2 ].up.set( 0, 0, - 1 );
144+
cameras[ 2 ].lookAt( 0, 1, 0 );
145+
cameras[ 3 ].up.set( 0, 0, 1 );
146+
cameras[ 3 ].lookAt( 0, - 1, 0 );
147+
cameras[ 4 ].up.set( 0, 1, 0 );
148+
cameras[ 4 ].lookAt( 0, 0, 1 );
149+
cameras[ 5 ].up.set( 0, 1, 0 );
150+
cameras[ 5 ].lookAt( 0, 0, - 1 );
151+
152+
for ( let i = 0; i < 6; i ++ ) cameras[ i ].updateMatrixWorld();
153+
154+
const geometry = new THREE.BoxGeometry( 5, 5, 5 );
155+
156+
const material = new THREE.ShaderMaterial( {
157+
name: 'FilterCubemap',
158+
uniforms: THREE.UniformsUtils.clone( CubemapFilterShader.uniforms ),
159+
vertexShader: CubemapFilterShader.vertexShader,
160+
fragmentShader: CubemapFilterShader.fragmentShader,
161+
side: THREE.BackSide,
162+
blending: THREE.NoBlending,
163+
} );
164+
165+
material.uniforms.cubeTexture.value = sourceCubeTexture;
166+
167+
const mesh = new THREE.Mesh( geometry, material );
168+
169+
const currentRenderTarget = renderer.getRenderTarget();
170+
const currentXrEnabled = renderer.xr.enabled;
171+
renderer.xr.enabled = false;
172+
173+
for ( let faceIndex = 0; faceIndex < 6; faceIndex ++ ) {
174+
175+
let mipIndex = 0;
176+
let mipSize = cubeMapRenderTarget.width;
177+
178+
// Render to each texture mip level
179+
while ( mipSize >= 1 ) {
180+
181+
cubeMapRenderTarget.viewport.set( 0, 0, mipSize, mipSize );
182+
renderer.setRenderTarget( cubeMapRenderTarget, faceIndex, mipIndex );
183+
material.uniforms.mipIndex.value = mipIndex;
184+
material.needsUpdate = true;
185+
renderer.render( mesh, cameras[ faceIndex ] );
186+
mipSize >>= 1;
187+
mipIndex ++;
188+
189+
}
190+
191+
}
192+
193+
renderer.setRenderTarget( currentRenderTarget );
194+
renderer.xr.enabled = currentXrEnabled;
195+
196+
mesh.geometry.dispose();
197+
mesh.material.dispose();
198+
199+
}
200+
201+
function init() {
202+
203+
container = document.createElement( 'div' );
204+
document.body.appendChild( container );
205+
206+
// Create renderer
207+
renderer = new THREE.WebGLRenderer( { antialias: true } );
208+
renderer.setPixelRatio( window.devicePixelRatio );
209+
renderer.setSize( window.innerWidth, window.innerHeight );
210+
container.appendChild( renderer.domElement );
211+
212+
scene = new THREE.Scene();
213+
214+
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10000 );
215+
camera.position.z = 500;
216+
217+
// Create controls
218+
const controls = new OrbitControls( camera, renderer.domElement );
219+
controls.minPolarAngle = Math.PI / 4;
220+
controls.maxPolarAngle = Math.PI / 1.5;
221+
222+
window.addEventListener( 'resize', onWindowResize );
223+
224+
// Load a cube texture
225+
const r = 'textures/cube/Park3Med/';
226+
const urls = [
227+
r + 'px.jpg', r + 'nx.jpg',
228+
r + 'py.jpg', r + 'ny.jpg',
229+
r + 'pz.jpg', r + 'nz.jpg'
230+
];
231+
232+
loadCubeTexture( urls ).then( ( cubeTexture ) => {
233+
234+
// Allocate a cube map render target
235+
const cubeMapRenderTarget = allocateCubemapRenderTarget( 512 );
236+
237+
// Render to all the mip levels of cubeMapRenderTarget
238+
renderToCubeTexture( cubeMapRenderTarget, cubeTexture );
239+
240+
// Create geometry
241+
const sphere = new THREE.SphereGeometry( 100, 128, 128 );
242+
let material = new THREE.MeshBasicMaterial( { color: 0xffffff, envMap: cubeTexture } );
243+
244+
let mesh = new THREE.Mesh( sphere, material );
245+
mesh.position.set( - 100, 0, 0 );
246+
scene.add( mesh );
247+
248+
material = material.clone();
249+
material.envMap = cubeMapRenderTarget.texture;
250+
251+
mesh = new THREE.Mesh( sphere, material );
252+
mesh.position.set( 100, 0, 0 );
253+
scene.add( mesh );
254+
255+
} );
256+
257+
}
258+
259+
function onWindowResize() {
260+
261+
camera.aspect = window.innerWidth / window.innerHeight;
262+
camera.updateProjectionMatrix();
263+
264+
renderer.setSize( window.innerWidth, window.innerHeight );
265+
266+
}
267+
268+
function animate() {
269+
270+
requestAnimationFrame( animate );
271+
renderer.render( scene, camera );
272+
273+
}
274+
275+
</script>
276+
277+
</body>
278+
</html>

src/renderers/WebGLRenderer.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,7 +2119,16 @@ class WebGLRenderer {
21192119

21202120
if ( renderTarget.isWebGLCubeRenderTarget ) {
21212121

2122-
framebuffer = __webglFramebuffer[ activeCubeFace ];
2122+
if ( Array.isArray( __webglFramebuffer[ activeCubeFace ] ) ) {
2123+
2124+
framebuffer = __webglFramebuffer[ activeCubeFace ][ activeMipmapLevel ];
2125+
2126+
} else {
2127+
2128+
framebuffer = __webglFramebuffer[ activeCubeFace ];
2129+
2130+
}
2131+
21232132
isCube = true;
21242133

21252134
} else if ( ( capabilities.isWebGL2 && renderTarget.samples > 0 ) && textures.useMultisampledRTT( renderTarget ) === false ) {
@@ -2128,7 +2137,15 @@ class WebGLRenderer {
21282137

21292138
} else {
21302139

2131-
framebuffer = __webglFramebuffer;
2140+
if ( Array.isArray( __webglFramebuffer ) ) {
2141+
2142+
framebuffer = __webglFramebuffer[ activeMipmapLevel ];
2143+
2144+
} else {
2145+
2146+
framebuffer = __webglFramebuffer;
2147+
2148+
}
21322149

21332150
}
21342151

0 commit comments

Comments
 (0)