Javascript webgl、纹理坐标和obj

Javascript webgl、纹理坐标和obj,javascript,webgl,Javascript,Webgl,我发现在渲染数据时很难理解顶点和纹理坐标之间的相关性。我使用从obj解析的DropeElements表单数据绘制了一个立方体。我得到的纹理接近于使用一个简单平面,其中位置和纹理坐标的顶点数,但一旦我使用更复杂的模型,甚至只是更复杂的uv展开,我最终会发现纹理出了问题 从我所读到的内容来看,没有一种使用纹理坐标索引的方法与使用顶点位置的方法相同,这很不幸,因为obj具有该信息。我让它接近工作的方式是从obj中的索引数据构建一个纹理坐标数组。但是,由于顶点和纹理坐标数组的长度不同,例如,在立方体的o

我发现在渲染数据时很难理解顶点和纹理坐标之间的相关性。我使用从obj解析的DropeElements表单数据绘制了一个立方体。我得到的纹理接近于使用一个简单平面,其中位置和纹理坐标的顶点数,但一旦我使用更复杂的模型,甚至只是更复杂的uv展开,我最终会发现纹理出了问题

从我所读到的内容来看,没有一种使用纹理坐标索引的方法与使用顶点位置的方法相同,这很不幸,因为obj具有该信息。我让它接近工作的方式是从obj中的索引数据构建一个纹理坐标数组。但是,由于顶点和纹理坐标数组的长度不同,例如,在立方体的obj中,有8个顶点,最多36个纹理坐标取决于网格是否展开,因此它们不相关


使用抽屉元素并将顶点映射到其正确的纹理坐标的正确工作流是什么。

如果您是正确的,您不能轻松地对案例位置和纹理坐标中的不同属性使用不同的索引

一个常见的例子是立方体。如果要使用照明渲染立方体,则需要法线。立方体上只有8个位置,但立方体的每个面都需要3条不同的法线用于相同的位置,共享该位置的每个面都需要一条法线。这意味着您总共需要24个顶点,立方体的6个面中的每个面需要4个顶点

如果文件格式对不同属性有单独的索引,则需要将其展开,以便每个属性的唯一组合位置、法线、纹理坐标等。。在你的缓冲区里

大多数游戏引擎都会在离线状态下做这种事情。换句话说,他们会编写一些工具来读取OBJ文件,展开各种属性,然后将数据写回预先展开的文件。这是因为,如果您试图优化数据并且只保留唯一的顶点,那么对于大型模型,在运行时生成扩展数据可能会非常耗时

如果您不关心最佳数据,那么只需根据索引展开即可。每种类型属性的索引数应相同

注:职位不特殊。我之所以提出这个问题,是因为你说没有一种方法可以像顶点位置那样使用纹理坐标索引。WebGL没有职位的概念。它只有描述如何从缓冲区中提取数据的属性。这些属性、位置、法线、随机数据中的内容由您决定。gl.DrainElements为您提供的整个属性组合编制索引。如果你传入一个索引7,它会给你每个属性的元素7

注意,上面描述了几乎所有用WebGL编写的3d引擎的工作原理。也就是说,如果你真的想,你可以发挥创造力

这是一个在纹理中存储位置和法线的程序。然后将索引放入缓冲区。因为纹理是随机访问的,所以它可以具有不同的位置和法线索引

var canvas=document.getElementByIdc; var gl=canvas.getContextwebgl; var ext=gl.getextensiones\u texture\u float; 如果分机{ 因为我懒惰,所以需要纹理浮动扩展; //返回; } 如果gl.getParametergl.MAX_顶点_纹理_图像_单位<2{ 需要能够从顶点着色器访问纹理; //返回; } var m4=twgl.m4; var v3=twgl.v3; var programInfo=twgl.createProgramInfogl[vshader,fshader]; //立方体数据 变量位置=[ -1,-1,-1,//0磅 +1,-1,-1,//1 rbb 2--3 -1,+1,-1,//2 ltb/|/| +1,+1,-1,//3RTB6--7| -1,-1,+1,//4磅力| +1,-1,+1,//5RBF | 0-|-1 -1,+1,+1,//6 ltf |/|/ +1,+1,+1,//7RTF4--5 ]; 风险价值指数=[ 3,7,5,3,5,1,//对 6,2,0,6,0,4,//左 6,7,3,6,3,2,//顶部 0,1,5,0,5,4,//底部 7,6,4,7,4,5,//前面 2,3,1,2,1,0,//返回 ]; 变量法线=[ +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, ] var指数=[ 0,0,0,0,0,0,0,//对 1,1,1,1,1,1,//左 2,2,2,2,2,2,//顶部 3,3,3,3,3,3,//底部 4,4,4,4,4,4,//前面 5,5,5,5,5,5,//回来 ]; 函数degToRaddeg{ 返回度*数学PI/180; } var bufferInfo=twgl.createBufferInfoFromArraysgl{ a_positionIndex:{size:1,data:positionIndex}, a_normalIndex:{size:1,data:normalIndex,}, }; var textures=twgl.createTexturesgl{ 职位:{ 格式:gl.RGB, 类型:gl.FLOAT, 身高:1,, src:职位, 最小值:gl最近值, 杂志:德国劳埃德船级社, 包裹:总包夹至边缘, }, 法线:{ 格式:gl.RGB, 类型:gl.FLOAT, 身高:1,, src:normals, 最小值:gl最近值, 杂志:德国劳埃德船级社, 包裹:总包夹至边缘, }, }; var xRot=degToRad30; var yRot=degToRad20; var lightDir=v3.normalize[-0.2,-0.1,0.5]; 函数提取时间{ 时间*=0.001;//转换为秒 twgl.resizeCanvasToDisplaySizegl.canvas; gl.viewport0,0,gl.canvas.width,gl.canvas.height; yRot=时间; gl.enablegl.DEPTH_测试; gl.enablegl.CULL_面; gl.useprograminfo.program; 变量persp=m4.0 degToRad45, gl.canvas.clientWidth/gl.canvas.clientHeight, 0.1, 100.0; var mat=m4.1; mat=m4.translatemat[0.0,0.0,-5.0]; mat=m4.rotateXmat,xRot; mat=m4。旋转mat,yRot; 变量={ u_位置:纹理。位置, u_positionsSize:[positions.length/3,1], u_法线:纹理。法线, u_normalsSize:[normals.length/3,1], u_MVP矩阵:m4.multiplypersp,mat, u_mvMatrix:mat, u_颜色:[0.5,0.8,1,1], u_lightDirection:lightDir, }; twgl.setBuffersAndAttributesgl、programInfo、bufferInfo; twgl.setuniformsprogramminfo,制服; twgl.drawBufferInfo,bufferInfo; 请求动画框架绘制; } 请求动画框架绘制; 正文{页边距:0;} 画布{宽度:100vw;高度:100vh;显示:块;} 属性浮动a_位置索引; 属性浮动a_normalIndex; 属性向量4 a_pos; 均匀采样2D u_位置; 统一的vec2 u_定位; 均匀采样2D u_法线; 均匀向量2 u_正规化; 均匀mat4 u_MVP矩阵; 均匀mat4u_矩阵; 可变vec3 v_正常; //要索引纹理中的值,我们需要 //计算将访问的纹理坐标 //正确的texel。要做到这一点,我们需要从 //从第一个texel的中间到第一个texel的中间 //最后一个特克斯。 // //换句话说,如果我们有3个值 //3特克斯我们会有这样的 // //---3x1---texel----- // [ ][ ][ ] // 0.0 || 1.0 // //如果我们只做索引/numvalue,我们会得到 // // [ ][ ][ ] // | | | // 0.0 0.333 0.666 // //正好在texel之间,所以我们添加了一个 //一个半特克塞尔来得到这个 // // [ ][ ][ ] // | | | // 0.167 0.5 0.833 //注意:在WebGL2中,我们可以只使用“textureFetch”` //它采用整数像素位置 vec2 texCoordFromIndexconst浮点索引,const vec2 textureSize{ vec2 colRow=vec2 modindex,textureSize.x,//column floorindex/textureSize.x;//行 返回vec2colRow+0.5/纹理化; } 真空总管{ vec2 ptc=texCoordFromIndexa_位置索引,u_位置大小; vec3位置=纹理2 du_位置,ptc.rgb; vec2 ntc=texCoordFromIndexa_normalIndex,u_NormalSize; vec3法线=纹理2 du_法线,ntc.rgb; gl_位置=u_MVP矩阵*向量4位置,1; v_normal=u_mvMatrix*vec4normal,0.xyz; } 精密中泵浮子; 均匀的vec4 u_颜色; 均匀vec3u_光方向; 可变vec3 v_正常; 真空总管{ 浮光=圆点 正常化EV_法线,u_光照方向*0.5+0.5; gl_FragColor=vec4u_color.rgb*灯光,u_color.a; }
您是正确的,您不能轻松地为案例位置和纹理坐标中的不同属性使用不同的索引

一个常见的例子是立方体。如果要使用照明渲染立方体,则需要法线。立方体上只有8个位置,但立方体的每个面都需要3条不同的法线用于相同的位置,共享该位置的每个面都需要一条法线。这意味着您总共需要24个顶点,立方体的6个面中的每个面需要4个顶点

如果文件格式对不同属性有单独的索引,则需要将其展开,以便每个属性的唯一组合位置、法线、纹理坐标等。。在你的缓冲区里

大多数游戏引擎都会在离线状态下做这种事情。换句话说,他们会编写一些工具来读取OBJ文件,展开各种属性,然后将数据写回预先展开的文件。这是因为,如果您试图优化数据并且只保留唯一的顶点,那么对于大型模型,在运行时生成扩展数据可能会非常耗时

如果您不关心最佳数据,那么只需根据索引展开即可。每种类型属性的索引数应相同

注:职位不特殊。我之所以提出这个问题,是因为你说没有一种方法可以像顶点位置那样使用纹理坐标索引。WebGL没有职位的概念。它只有描述如何从缓冲区中提取数据的属性。这些属性、位置、法线、随机数据中的内容由您决定。gl.DrainElements为您提供的整个属性组合编制索引。如果你传入一个索引7,它会给你每个属性的元素7

请注意,上面描述的是 几乎所有用WebGL编写的3d引擎都能正常工作。也就是说,如果你真的想,你可以发挥创造力

这是一个在纹理中存储位置和法线的程序。然后将索引放入缓冲区。因为纹理是随机访问的,所以它可以具有不同的位置和法线索引

var canvas=document.getElementByIdc; var gl=canvas.getContextwebgl; var ext=gl.getextensiones\u texture\u float; 如果分机{ 因为我懒惰,所以需要纹理浮动扩展; //返回; } 如果gl.getParametergl.MAX_顶点_纹理_图像_单位<2{ 需要能够从顶点着色器访问纹理; //返回; } var m4=twgl.m4; var v3=twgl.v3; var programInfo=twgl.createProgramInfogl[vshader,fshader]; //立方体数据 变量位置=[ -1,-1,-1,//0磅 +1,-1,-1,//1 rbb 2--3 -1,+1,-1,//2 ltb/|/| +1,+1,-1,//3RTB6--7| -1,-1,+1,//4磅力| +1,-1,+1,//5RBF | 0-|-1 -1,+1,+1,//6 ltf |/|/ +1,+1,+1,//7RTF4--5 ]; 风险价值指数=[ 3,7,5,3,5,1,//对 6,2,0,6,0,4,//左 6,7,3,6,3,2,//顶部 0,1,5,0,5,4,//底部 7,6,4,7,4,5,//前面 2,3,1,2,1,0,//返回 ]; 变量法线=[ +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, 0, 0, 0, +1, 0, 0, -1, ] var指数=[ 0,0,0,0,0,0,0,//对 1,1,1,1,1,1,//左 2,2,2,2,2,2,//顶部 3,3,3,3,3,3,//底部 4,4,4,4,4,4,//前面 5,5,5,5,5,5,//回来 ]; 函数degToRaddeg{ 返回度*数学PI/180; } var bufferInfo=twgl.createBufferInfoFromArraysgl{ a_positionIndex:{size:1,data:positionIndex}, a_normalIndex:{size:1,data:normalIndex,}, }; var textures=twgl.createTexturesgl{ 职位:{ 格式:gl.RGB, 类型:gl.FLOAT, 身高:1,, src:职位, 最小值:gl最近值, 杂志:德国劳埃德船级社, 包裹:总包夹至边缘, }, 法线:{ 格式:gl.RGB, 类型:gl.FLOAT, 身高:1,, src:normals, 最小值:gl最近值, 杂志:德国劳埃德船级社, 包裹:总包夹至边缘, }, }; var xRot=degToRad30; var yRot=degToRad20; var lightDir=v3.normalize[-0.2,-0.1,0.5]; 函数提取时间{ 时间*=0.001;//转换为秒 twgl.resizeCanvasToDisplaySizegl.canvas; gl.viewport0,0,gl.canvas.width,gl.canvas.height; yRot=时间; gl.enablegl.DEPTH_测试; gl.enablegl.CULL_面; gl.useprograminfo.program; 变量persp=m4.0 degToRad45, gl.canvas.clientWidth/gl.canvas.clientHeight, 0.1, 100.0; var mat=m4.1; mat=m4.translatemat[0.0,0.0,-5.0]; mat=m4.rotateXmat,xRot; mat=m4。旋转mat,yRot; 变量={ u_位置:纹理。位置, u_positionsSize:[positions.length/3,1], u_法线:纹理。法线, u_normalsSize:[normals.length/3,1], u_MVP矩阵:m4.multiplypersp,mat, u_mvMatrix:mat, u_颜色:[0.5,0.8,1,1], u_lightDirection:lightDir, }; twgl.setBuffersAndAttributesgl、programInfo、bufferInfo; twgl.setuniformsprogramminfo,制服; twgl.drawBufferInfo,bufferInfo; 请求动画框架绘制; } 请求动画框架绘制; 正文{页边距:0;} 画布{宽度:100vw;高度:100vh;显示:块;} 属性浮动a_位置索引; 属性浮动a_normalIndex; 属性向量4 a_pos; 均匀采样2D u_位置; 统一的vec2 u_定位; 均匀采样2D u_法线; 均匀向量2 u_正规化; 均匀mat4 u_MVP矩阵; 均匀mat4u_矩阵; 可变vec3 v_正常; //要索引纹理中的值,我们需要 //计算将访问的纹理坐标 //正确的texel。要做到这一点,我们需要从 //从第一个texel的中间到第一个texel的中间 //最后一个特克斯。 // //换句话说,如果我们有3个值 //3特克斯我们会有这样的 // //---3x1---texel----- // [ ][ ][ ] // 0.0 || 1.0 // //如果我们只做索引/numvalue,我们会得到 // // [ ][ ][ ] // | | | // 0.0 0.333 0.666 // //正好在texel之间,所以我们添加了一个 //一个半特克塞尔来得到这个 // // [ ][ ][ ] // | | | // 0.167 0.5 0.833 //注意:在WebGL2中,我们可以只使用“textureFetch”` //它采用整数像素位置 vec2 texCoordFromIndexconst浮点索引,const vec2 textureSize{ vec2 colRow=vec2 modindex,textureSize.x,//column floorindex/textureSize.x;//行 返回vec2colRow+0.5/纹理化; } 真空总管{ vec2 ptc=texCoordFromIndexa_位置索引,u_位置大小; vec3位置=纹理2 du_位置,ptc.rgb; vec2 ntc=texCoordFromIndexa_normalIndex,u_NormalSize; vec3法线=纹理2 du_法线,ntc.rgb; gl_位置=u_MVP矩阵*向量4位置,1 ; v_normal=u_mvMatrix*vec4normal,0.xyz; } 精密中泵浮子; 均匀的vec4 u_颜色; 均匀vec3u_光方向; 可变vec3 v_正常; 真空总管{ 浮光=圆点 正常化EV_法线,u_光照方向*0.5+0.5; gl_FragColor=vec4u_color.rgb*灯光,u_color.a; }
以立方体为例,因为每个顶点有3条法线,所以需要在3次内添加每个顶点,以使用映射到法线缓冲区中相应法线的索引。因此,实际上使用多维数据集drawElements不会比DrawArray有任何性能提升。对于多维数据集,您的建议是正确的。对于球体,尽管顶点将具有相同的法线。对于多面立方体、建筑物、盒子、板条箱之类的东西,是的,索引并不能给你带来任何东西。对于平滑的事物,地球、树木、动物、人类、生物索引帮助很大。因为平滑的东西通常有更多的顶点,所以这可能是一个胜利。让我补充一点,你可以做一些疯狂的事情。您可以在纹理中存储位置、法线、纹理坐标等。然后使用属性作为这些纹理的索引。这样,顶点的每个部分都可以有单独的索引。不幸的是,对纹理的随机访问可能很慢。GPU希望纹理的读取是相对线性的,因此非线性读取纹理可能会导致缓存未命中,从而影响性能。你必须进行测试,看看是否值得尝试这条路线。事实上,即使是一个立方体,你仍然可以获得收益。每个面有两个共享两个顶点的三角形。要绘制立方体,您需要绘制12个三角形或36个顶点,但索引后只需要24个唯一顶点。。但是我不推荐这种方法。所以以立方体为例,因为每个顶点有3条法线,所以需要在3次内添加每个顶点,以使用映射到法线缓冲区中相应法线的索引。因此,实际上使用多维数据集drawElements不会比DrawArray有任何性能提升。对于多维数据集,您的建议是正确的。对于球体,尽管顶点将具有相同的法线。对于多面立方体、建筑物、盒子、板条箱之类的东西,是的,索引并不能给你带来任何东西。对于平滑的事物,地球、树木、动物、人类、生物索引帮助很大。因为平滑的东西通常有更多的顶点,所以这可能是一个胜利。让我补充一点,你可以做一些疯狂的事情。您可以在纹理中存储位置、法线、纹理坐标等。然后使用属性作为这些纹理的索引。这样,顶点的每个部分都可以有单独的索引。不幸的是,对纹理的随机访问可能很慢。GPU希望纹理的读取是相对线性的,因此非线性读取纹理可能会导致缓存未命中,从而影响性能。你必须进行测试,看看是否值得尝试这条路线。事实上,即使是一个立方体,你仍然可以获得收益。每个面有两个共享两个顶点的三角形。要绘制立方体,您需要绘制12个三角形或36个顶点,但索引后只需要24个唯一顶点。。不过我不推荐这种方法。