繁体   English   中英

GLSL:将可变数量的纹理发送到sampler2D

[英]GLSL: Sending variable number of textures to sampler2D

我想将可变的纹理数组发送给某些着色器,但不知道如何在统一的sampler2D中可变纹理的数量。

例如,在下面的示例中,我将两个纹理发送到着色器,并尝试初始化长度为2的sampler2D均匀值texture 。但是,这会产生以下异常:

THREE.WebGLProgram:着色器错误:0 gl.VALIDATE_STATUS否gl.getProgramInfoLog无效的着色器错误:0:125:''​​:需要常量表达式错误:0:125:''​​:数组大小必须是常量整数表达式错误:0: 134:'[]':数组索引超出范围

是否可以可变化要发送到sampler2D统一值的纹理数量? 对于其他人可以在这个问题上提供的任何见解,我将不胜感激!

 /** * Generate a scene object with a background color **/ function getScene() { var scene = new THREE.Scene(); scene.background = new THREE.Color(0xffffff); return scene; } /** * Generate the camera to be used in the scene. Camera args: * [0] field of view: identifies the portion of the scene * visible at any time (in degrees) * [1] aspect ratio: identifies the aspect ratio of the * scene in width/height * [2] near clipping plane: objects closer than the near * clipping plane are culled from the scene * [3] far clipping plane: objects farther than the far * clipping plane are culled from the scene **/ function getCamera() { var aspectRatio = window.innerWidth / window.innerHeight; var camera = new THREE.PerspectiveCamera(75, aspectRatio, 0.1, 1000); camera.position.set(0, 1, 10); return camera; } /** * Generate the renderer to be used in the scene **/ function getRenderer() { // Create the canvas with a renderer var renderer = new THREE.WebGLRenderer({antialias: true}); // Add support for retina displays renderer.setPixelRatio(window.devicePixelRatio); // Specify the size of the canvas renderer.setSize(window.innerWidth, window.innerHeight); // Add the canvas to the DOM document.body.appendChild(renderer.domElement); return renderer; } /** * Generate the controls to be used in the scene * @param {obj} camera: the three.js camera for the scene * @param {obj} renderer: the three.js renderer for the scene **/ function getControls(camera, renderer) { var controls = new THREE.TrackballControls(camera, renderer.domElement); controls.zoomSpeed = 0.4; controls.panSpeed = 0.4; return controls; } /** * Load image **/ function loadImage() { var geometry = new THREE.BufferGeometry(); /* Now we need to push some vertices into that geometry to identify the coordinates the geometry should cover */ // Identify the image size var imageSize = {width: 10, height: 7.5}; // Identify the x, y, z coords where the image should be placed var coords = {x: -5, y: -3.75, z: 0}; // Add one vertex for each corner of the image, using the // following order: lower left, lower right, upper right, upper left var vertices = new Float32Array([ coords.x, coords.y, coords.z, // bottom left coords.x+imageSize.width, coords.y, coords.z, // bottom right coords.x+imageSize.width, coords.y+imageSize.height, coords.z, // upper right coords.x, coords.y+imageSize.height, coords.z, // upper left ]) // set the uvs for this box; these identify the following corners: // lower-left, lower-right, upper-right, upper-left var uvs = new Float32Array([ 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, ]) // store the texture index of each object to be rendered var textureIndices = new Float32Array([0.0, 0.0, 0.0, 0.0]); // indices = sequence of index positions in `vertices` to use as vertices // we make two triangles but only use 4 distinct vertices in the object // the second argument to THREE.BufferAttribute is the number of elements // in the first argument per vertex geometry.setIndex([0,1,2, 2,3,0]) geometry.addAttribute('position', new THREE.BufferAttribute(vertices, 3)); geometry.addAttribute('uv', new THREE.BufferAttribute(uvs, 2)); geometry.addAttribute('textureIdx', new THREE.BufferAttribute(textureIndices, 1)); // Create a texture loader so we can load our image file var loader = new THREE.TextureLoader(); // specify the url to the texture var catUrl = 'https://s3.amazonaws.com/duhaime/blog/tsne-webgl/assets/cat.jpg'; var dogUrl = 'https://s3.amazonaws.com/duhaime/blog/tsne-webgl/assets/dog.jpg'; // get an array of textures var textureArr = [loader.load(catUrl), loader.load(dogUrl)]; // specify custom uniforms and attributes for shaders // Uniform types: https://github.com/mrdoob/three.js/wiki/Uniforms-types var material = new THREE.ShaderMaterial({ uniforms: { verticesPerTexture: new Float32Array([4.0]), // store the count of vertices per texture texture_num: { type: 'i', value: textureArr.length, }, cat_texture: { type: 't', value: loader.load(catUrl), }, dog_texture: { type: 't', value: loader.load(dogUrl), }, textures: { type: 'tv', // type for texture array value: textureArr, } }, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent }); // Combine our image geometry and material into a mesh var mesh = new THREE.Mesh(geometry, material); // Set the position of the image mesh in the x,y,z dimensions mesh.position.set(0,0,0) // Add the image to the scene scene.add(mesh); } /** * Render! **/ function render() { requestAnimationFrame(render); renderer.render(scene, camera); controls.update(); }; var scene = getScene(); var camera = getCamera(); var renderer = getRenderer(); var controls = getControls(camera, renderer); loadImage(); render(); 
 html, body { width: 100%; height: 100%; background: #000; } body { margin: 0; overflow: hidden; } canvas { width: 100%; height: 100%; } 
 <script src='https://cdnjs.cloudflare.com/ajax/libs/three.js/92/three.min.js'></script> <script src='https://threejs.org/examples/js/controls/TrackballControls.js'></script> <script type='x-shader/x-vertex' id='vertex-shader'> /** * The vertex shader's main() function must define `gl_Position`, * which describes the position of each vertex in the space. * * To do so, we can use the following variables defined by Three.js: * * uniform mat4 modelViewMatrix - combines: * model matrix: maps a point's local coordinate space into world space * view matrix: maps world space into camera space * * uniform mat4 projectionMatrix - maps camera space into screen space * * attribute vec3 position - sets the position of each vertex * * attribute vec2 uv - determines the relationship between vertices and textures * * `uniforms` are constant across all vertices * * `attributes` can vary from vertex to vertex and are defined as arrays * with length equal to the number of vertices. Each index in the array * is an attribute for the corresponding vertex * * `varyings` are values passed from the vertex to the fragment shader * * Specifying attributes that are not passed to the vertex shader will not pevent shader compiling **/ // declare attributes attribute float textureIdx; // declare uniform vals uniform float verticesPerTexture; // store the vertices per texture // declare variables to pass to fragment shaders varying vec2 vUv; // pass the uv coordinates of each vertex to the frag shader varying float vTextureIdx; // pass the texture idx void main() { vTextureIdx = textureIdx; vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } </script> <script type='x-shader/x-fragment' id='fragment-shader'> /** * The fragment shader's main() function must define `gl_FragColor`, * which describes the pixel color of each pixel on the screen. * * To do so, we can use uniforms passed into the shader and varyings * passed from the vertex shader * * Attempting to read a varying not generated by the vertex shader will * throw a warning but won't prevent shader compiling * * Each attribute must contain n_vertices * n_components, where n_components * is the length of the given datatype (eg vec2 n_components = 2; * float n_components = 1) **/ precision highp float; // set float precision (optional) varying vec2 vUv; // identify the uv values as a varying attribute varying float vTextureIdx; // identify the texture indices as a varying attribute uniform int texture_num; // identify the number of textures used uniform sampler2D cat_texture; // identify the texture as a uniform argument uniform sampler2D dog_texture; // identify the texture as a uniform argument uniform sampler2D textures[texture_num]; void main() { int textureIndex = int(floor(vTextureIdx)); //sampler2D texture = textures[textureIndex]; this is not allowed :/ if (int(floor(vTextureIdx)) == 0) { gl_FragColor = texture2D(textures[0], vUv); } else { gl_FragColor = texture2D(textures[1], vUv); } } </script> 

GLSL是一种类似C的语言,并且不能使用用于大小的变量来分配C数组。

相反,您可以动态生成着色器代码字符串,以将许多纹理集成到代码中。 对于您的情况,您将需要进行一些替换。 作为最小的示例:

  var material = new THREE.ShaderMaterial({ uniforms: { verticesPerTexture: new Float32Array([4.0]), // store the count of vertices per texture cat_texture: { type: 't', value: loader.load(catUrl), }, dog_texture: { type: 't', value: loader.load(dogUrl), }, textures: { type: 'tv', // type for texture array value: textureArr, } }, vertexShader: document.getElementById('vertex-shader').textContent, fragmentShader: document.getElementById('fragment-shader').textContent.replace(/!!!_TEXCOUNT_!!!/g,textureArr.length.toString()); }); 
 <script type='x-shader/x-fragment' id='fragment-shader'> precision highp float; // set float precision (optional) varying vec2 vUv; // identify the uv values as a varying attribute varying float vTextureIdx; // identify the texture indices as a varying attribute // you don't need cat_texture and dog_texture because they're in the following array uniform sampler2D textures[!!!_TEXCOUNT_!!!]; void main() { int textureIndex = int(floor(vTextureIdx)); if (int(floor(vTextureIdx)) == 0) { gl_FragColor = texture2D(textures[0], vUv); } else { gl_FragColor = texture2D(textures[1], vUv); } } </script> 

替换项寻找密钥!!!_TEXCOUNT_!!! 并将其替换为数组中实际的纹理数量。

注意,我也完全删除了texture_num制服,因为不再需要它。 我还删除了cat_texturedog_texture因为您的纹理现在保存在textures数组中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM