如何为三角形带构建的14顶点立方体设置纹理坐标?
绝对可以搞定!首先得明确你这个14顶点三角形带立方体的顶点结构——这类立方体一般是按环形顺序构建的,用最少的顶点生成6个面。核心思路是先划分你的纹理图集的UV区域,再给每个顶点(或根据顶点所在的面)分配对应的UV坐标。下面给你一步步拆解:
第一步:定义纹理图集的UV区域
假设你的纹理图是纵向划分的(你可以根据实际调整):
- 顶面:占据纹理的上1/3,UV范围是
(0.0, 2/3)到(1.0, 1.0) - 底面:占据纹理的中1/3,UV范围是
(0.0, 1/3)到(1.0, 2/3) - 四个侧面:共享纹理的下1/3,UV范围是
(0.0, 0.0)到(1.0, 1/3)
如果你的纹理是横向划分的,只需要把UV的x和y范围调换即可,比如顶面占左1/3,底面中1/3,侧面右1/3。
第二步:给顶点分配UV(两种方案)
方案1:CPU端预生成UV数据(简单直接)
如果你的14顶点顺序是标准的三角形带立方体(底面4个→顶面4个→闭合用的6个),可以直接给每个顶点绑定对应面的UV:
假设你的立方体是轴对齐的,中心在原点,边长为1(坐标范围 -0.5 到 0.5),伪代码示例:
// 定义各区域的UV边界 struct UVRegion { float uMin, uMax; float vMin, vMax; }; UVRegion top = {0.0f, 1.0f, 2.0f/3.0f, 1.0f}; UVRegion bottom = {0.0f, 1.0f, 1.0f/3.0f, 2.0f/3.0f}; UVRegion side = {0.0f, 1.0f, 0.0f, 1.0f/3.0f}; // 14个顶点的UV数组 vec2 uvs[14]; // 底面顶点(索引0-3:左前、右前、左后、右后) uvs[0] = {bottom.uMin, bottom.vMin}; uvs[1] = {bottom.uMax, bottom.vMin}; uvs[2] = {bottom.uMin, bottom.vMax}; uvs[3] = {bottom.uMax, bottom.vMax}; // 顶面顶点(索引4-7:左后、右后、左前、右前) uvs[4] = {top.uMin, top.vMin}; uvs[5] = {top.uMax, top.vMin}; uvs[6] = {top.uMin, top.vMax}; uvs[7] = {top.uMax, top.vMax}; // 侧面闭合顶点(索引8-13:右前、右后、右后、左后、左后、左前) // 这些顶点属于侧面,直接用侧面区域的UV uvs[8] = {side.uMin, side.vMin}; uvs[9] = {side.uMax, side.vMin}; uvs[10] = {side.uMin, side.vMax}; uvs[11] = {side.uMax, side.vMax}; uvs[12] = {side.uMin, side.vMin}; uvs[13] = {side.uMax, side.vMin};
如果你的顶点顺序不同,只需要对应调整每个索引的UV即可——核心是判断每个顶点属于顶面、底面还是侧面,然后映射到对应的纹理区域。
方案2:几何着色器中动态计算UV(灵活适配)
如果你的顶点顺序不固定,或者想在GPU端处理,可以在几何着色器中根据顶点的位置/法向量判断所属面,动态计算UV:
#version 330 core layout (triangles_adjacency) in; layout (triangle_strip, max_vertices = 14) out; out vec2 TexCoords; // 纹理区域定义(根据你的实际图集调整) const vec2 TOP_UV_MIN = vec2(0.0, 2.0/3.0); const vec2 TOP_UV_MAX = vec2(1.0, 1.0); const vec2 BOTTOM_UV_MIN = vec2(0.0, 1.0/3.0); const vec2 BOTTOM_UV_MAX = vec2(1.0, 2.0/3.0); const vec2 SIDE_UV_MIN = vec2(0.0, 0.0); const vec2 SIDE_UV_MAX = vec2(1.0, 1.0/3.0); // 立方体的尺寸(假设中心在原点,边长1) const float HALF_SIZE = 0.5; void main() { for(int i = 0; i < gl_in.length(); i++) { vec4 worldPos = gl_in[i].gl_Position; vec2 uv; // 判断顶点所属面 if(abs(worldPos.y - HALF_SIZE) < 0.01) { // 顶面 // 用X/Z坐标映射到0-1的局部UV float u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE); float v = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE); uv = vec2( mix(TOP_UV_MIN.x, TOP_UV_MAX.x, u), mix(TOP_UV_MIN.y, TOP_UV_MAX.y, v) ); } else if(abs(worldPos.y + HALF_SIZE) < 0.01) { // 底面 float u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE); float v = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE); uv = vec2( mix(BOTTOM_UV_MIN.x, BOTTOM_UV_MAX.x, u), mix(BOTTOM_UV_MIN.y, BOTTOM_UV_MAX.y, v) ); } else { // 侧面 // 根据X/Z的绝对值判断是左右还是前后侧面,计算局部UV float u; if(abs(worldPos.x) > HALF_SIZE - 0.01) { // 左右侧面,用Z坐标做U u = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE); } else { // 前后侧面,用X坐标做U u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE); } // 用Y坐标做V float v = (worldPos.y + HALF_SIZE) / (2 * HALF_SIZE); uv = vec2( mix(SIDE_UV_MIN.x, SIDE_UV_MAX.x, u), mix(SIDE_UV_MIN.y, SIDE_UV_MAX.y, v) ); } TexCoords = uv; gl_Position = worldPos; EmitVertex(); } EndPrimitive(); }
常见问题排查
- 纹理翻转:如果某个面的纹理是倒的,只需要把对应的UV分量取反(比如
1.0 - u或1.0 - v)。 - 边缘拉伸:确保纹理的Wrap模式设置为
CLAMP_TO_EDGE,避免纹理边缘的重复采样。 - 浮点误差:用顶点法向量判断面比用位置更可靠——如果你的顶点属性包含法向量,直接判断法向量的轴向(比如法向量Y=1是顶面)即可。
内容的提问来源于stack exchange,提问作者Adrien Givry
相关产品推荐
相关产品推荐

