Webgl, coordonnées de texture et obj

Je trouve difficile de comprendre la corrélation entre les coordonnées de vertex et de texture lorsque les données sont rendues. J'ai un cube dessiné à l'aide des données de forme drawElements analysées à partir d'un obj. J'ai eu des textures quelque part près de travailler avec un plan simple où le nombre de sommets pour la position et pour la texture se coordonne, mais une fois que j'utilise un modèle plus complexe ou même un déroulement uv plus complexe, je finis par la mauvaise impression de la texture.

D'après ce que j'ai lu, il ne s'agit pas d'utiliser des index de coordonnées de texture de la même façon que pour la position vertex, ce qui est regrettable car l'obj a cette information. La façon dont je l'ai achevé de travailler était en construisant une série de coordonnées de texture à partir des données d'indices dans l'obj. Mais parce que la longueur des tableaux de coordonnées de sommet et de texture diffèrent (par exemple, dans un obj pour un cube, il existe 8 points de vertex et jusqu'à 36 coordonnées de texture selon que le maillage est déballé), ils ne sont pas en corrélation.

Quel est le bon flux de travail pour utiliser drawElements et mapper le sommet à ses coordonnées de texture correctes.

Vous avez raison, vous ne pouvez pas utiliser facilement différents indices pour différents attributs (dans les positions de votre cas et les coordonnées de texture).

Un exemple commun est un cube. Si vous souhaitez rendre un cube avec éclairage, vous avez besoin de normal. Il n'y a que 8 positions sur un cube mais chaque face du cube a besoin de 3 normales différentes pour les mêmes positions, une normale pour chaque visage qui partage cette position. Cela signifie que vous avez besoin de 24 sommets au total, 4 pour chacun des 6 visages du cube.

Si vous disposez d'un format de fichier comportant des indices distincts pour différents attributs, vous devrez les étendre afin que chaque combinaison unique d'attributs (position, normal, coord de la texture, etc.) se trouve dans vos tampons.

La plupart des moteurs de jeux feraient ce genre de chose hors ligne. En d'autres termes, ils écrivent un outil qui lit le fichier OBJ, élargit les différents attributs, puis écrit les données en arrière pré-développées. C'est parce que générer des données étendues peut prendre du temps à l'exécution d'un grand modèle si vous essayez d'optimiser les données et de ne garder que des sommets uniques.

Si vous ne vous souciez pas des données optimales, développez simplement en fonction des indices. Le nombre d'indices pour chaque type d'attribut devrait être le même.

Note: les positions ne sont pas spéciales. J'apporte cela parce que vous dites qu'il n'y a pas moyen d'utiliser des index de coordonnées de texture de la même façon que pour la position vertex . WebGL n'a pas de concept de «postes». Il a juste des attributs qui décrivent comment extraire les données des tampons. Ce qui dépend de ces attributs (positions, normales, données aléatoires, quoi que ce soit) dépend de vous. gl.drawElements indexe toute la combinaison des attributs que vous fournissez. Si vous passez un index de 7, il vous donnera l'élément 7 de chaque attribut.

Notez que ce qui précède décrit comment presque tous les moteurs 3d écrits dans WebGL fonctionnent. Cela dit, vous pouvez devenir créatif si vous le souhaitez.

Voici un programme qui stocke les positions et les normales dans les textures. Il place ensuite les indices dans les tampons. Parce que les textures sont accessibles au hasard, il peut donc avoir différents indices pour les positions et les normales

 var canvas = document.getElementById("c"); var gl = canvas.getContext("webgl"); var ext = gl.getExtension("OES_texture_float"); if (!ext) { alert("need OES_texture_float extension cause I'm lazy"); //return; } if (gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) < 2) { alert("need to be able to access textures from vertex shaders"); //return; } var m4 = twgl.m4; var v3 = twgl.v3; var programInfo = twgl.createProgramInfo(gl, ["vshader", "fshader"]); // Cube data var positions = [ -1, -1, -1, // 0 lbb +1, -1, -1, // 1 rbb 2---3 -1, +1, -1, // 2 ltb /| /| +1, +1, -1, // 3 rtb 6---7 | -1, -1, +1, // 4 lbf | | | | +1, -1, +1, // 5 rbf | 0-|-1 -1, +1, +1, // 6 ltf |/ |/ +1, +1, +1, // 7 rtf 4---5 ]; var positionIndices = [ 3, 7, 5, 3, 5, 1, // right 6, 2, 0, 6, 0, 4, // left 6, 7, 3, 6, 3, 2, // top 0, 1, 5, 0, 5, 4, // bottom 7, 6, 4, 7, 4, 5, // front 2, 3, 1, 2, 1, 0, // back ]; var normals = [ +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, ] var normalIndices = [ 0, 0, 0, 0, 0, 0, // right 1, 1, 1, 1, 1, 1, // left 2, 2, 2, 2, 2, 2, // top 3, 3, 3, 3, 3, 3, // bottom 4, 4, 4, 4, 4, 4, // front 5, 5, 5, 5, 5, 5, // back ]; function degToRad(deg) { return deg * Math.PI / 180; } var bufferInfo = twgl.createBufferInfoFromArrays(gl, { a_positionIndex: { size: 1, data: positionIndices }, a_normalIndex: { size: 1, data: normalIndices, }, }); var textures = twgl.createTextures(gl, { positions: { format: gl.RGB, type: gl.FLOAT, height: 1, src: positions, min: gl.NEAREST, mag: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, }, normals: { format: gl.RGB, type: gl.FLOAT, height: 1, src: normals, min: gl.NEAREST, mag: gl.NEAREST, wrap: gl.CLAMP_TO_EDGE, }, }); var xRot = degToRad(30); var yRot = degToRad(20); var lightDir = v3.normalize([-0.2, -0.1, 0.5]); function draw(time) { time *= 0.001; // convert to seconds twgl.resizeCanvasToDisplaySize(gl.canvas); gl.viewport(0, 0, gl.canvas.width, gl.canvas.height); yRot = time; gl.enable(gl.DEPTH_TEST); gl.enable(gl.CULL_FACE); gl.useProgram(programInfo.program); var persp = m4.perspective( degToRad(45), gl.canvas.clientWidth / gl.canvas.clientHeight, 0.1, 100.0); var mat = m4.identity(); mat = m4.translate(mat, [0.0, 0.0, -5.0]); mat = m4.rotateX(mat, xRot); mat = m4.rotateY(mat, yRot); var uniforms = { u_positions: textures.positions, u_positionsSize: [positions.length / 3, 1], u_normals: textures.normals, u_normalsSize: [normals.length / 3, 1], u_mvpMatrix: m4.multiply(persp, mat), u_mvMatrix: mat, u_color: [0.5, 0.8, 1, 1], u_lightDirection: lightDir, }; twgl.setBuffersAndAttributes(gl, programInfo, bufferInfo); twgl.setUniforms(programInfo, uniforms); twgl.drawBufferInfo(gl, bufferInfo); requestAnimationFrame(draw); } requestAnimationFrame(draw); 
 body { margin: 0; } canvas { width: 100vw; height: 100vh; display: block; } 
 <script src="//twgljs.org/dist/2.x/twgl-full.min.js"></script> <script id="vshader" type="whatever"> attribute float a_positionIndex; attribute float a_normalIndex; attribute vec4 a_pos; uniform sampler2D u_positions; uniform vec2 u_positionsSize; uniform sampler2D u_normals; uniform vec2 u_normalsSize; uniform mat4 u_mvpMatrix; uniform mat4 u_mvMatrix; varying vec3 v_normal; // to index the value in the texture we need to // compute a texture coordinate that will access // the correct texel. To do that we need access from // the middle of the first texel to the middle of the // last texel. // // In other words if we had 3 values (and therefore // 3 texels) we'd have something like this // // ------3x1 ----- texels ---------- // [ ][ ][ ] // 0.0 |<----------------------------->| 1.0 // // If we just did index / numValues we'd get // // [ ][ ][ ] // | | | // 0.0 0.333 0.666 // // Which is right between texels so we add a // a halfTexel to get this // // [ ][ ][ ] // | | | // 0.167 0.5 0.833 // note: In WebGL2 we could just use `textureFetch` // which takes integer pixel locations vec2 texCoordFromIndex(const float index, const vec2 textureSize) { vec2 colRow = vec2( mod(index, textureSize.x), // columm floor(index / textureSize.x)); // row return vec2((colRow + 0.5) / textureSize); } void main() { vec2 ptc = texCoordFromIndex(a_positionIndex, u_positionsSize); vec3 position = texture2D(u_positions, ptc).rgb; vec2 ntc = texCoordFromIndex(a_normalIndex, u_normalsSize); vec3 normal = texture2D(u_normals, ntc).rgb; gl_Position = u_mvpMatrix * vec4(position, 1); v_normal = (u_mvMatrix * vec4(normal, 0)).xyz; } </script> <script id="fshader" type="whatever"> precision mediump float; uniform vec4 u_color; uniform vec3 u_lightDirection; varying vec3 v_normal; void main() { float light = dot( normalize(v_normal), u_lightDirection) * 0.5 + 0.5; gl_FragColor = vec4(u_color.rgb * light, u_color.a); } </script> <canvas id="c"></canvas>