所有 demo 和代码可以在 https://www.shadertoy.com/view/X3sBR7 查看
在传统 3D里面,纹理都是离线制作好,结合物体的形状生成的 UV贴图。Shadertoy中我们一切都是实时生成的。也就是我们需要更具实时生成出来的形状 找到对应面的纹理坐标。
平面映射
平面映射最简单不过了,在三维空间中,每一个顶点都有其三维坐标 (x, y, z)。平面映射通常围绕特定的平面,例如 XY、XZ 或 YZ 平面,选择一个平面作为纹理的展示区域。对于每个三维顶点,通过投影到选定的二维平面生成相应的纹理坐标 (u, v)。例如,如果选择 XY 平面,则可以通过下面的方式生成纹理坐标:
u = x
v = y
vec2 uv = p.xz;
color = texture(iChannel2,uv ).xyz * dif;
立方体映射
立方体映射是通过一组六个纹理(分别对应立方体的六个面)来表示一个环境。在渲染一个物体时,可以根据视线方向选择这六个面中的某一个进行采样。立方体纹理实际上是一个立方体的六个面,每个面都是一个二维纹理。通常,这六个面分别表示正X、负X、正Y、负Y、正Z和负Z方向。
立方体映射和平面映射几乎一样,只是需要乘上立方体表面的法线。
vec3 cXZ = texture(iChannel2, p.xz ).xyz ;
vec3 cYZ = texture(iChannel2, p.yz ).xyz ;
vec3 cXY = texture(iChannel2, p.xy ).xyz ;
color = cXZ * abs(n.y)+ cXY * abs(n.z) + cYZ * abs(n.x);
Cylindrical Mapping
圆柱坐标系的映射关系通常是这样的:
x = r * cos(0)
y = h
z = r * sin(0)
映射方法如下
float theta = atan(p.z, p.x); // angle around the Y-axis
float u = 0.5 + theta / (2.0 * 3.14159); // map theta to [0,1]
float v = (p.y + cylinderHeight / 2.0) / cylinderHeight; // map height to [0,1]
Triplanar Mapping
Triplanar Mapping 旨在减少纹理拉伸现象,特别是在复杂表面(如不规则地形或有些许凹凸的模型)上效果更佳。相较于传统的UV贴图,Triplanar Mapping 结合了三个方向的纹理,从而不依赖于UV坐标的定义。Triplanar Mapping 的核心思想是根据法线的方向将纹理应用于物体的三个主面(X、Y、Z 轴)。以下是其基本步骤和原理:
法线计算:
- 对于模型的每一个片段,计算其法线向量。法线为表面的每一点指向外侧的单位向量。
纹理坐标生成:
- X方向(并适用于法线的Y和Z分量)
- Y方向(并适用于法线的X和Z分量)
- Z方向(并适用于法线的X和Y分量)
- 对于每个片段,使用法线向量生成三个纹理坐标:
纹理混合:
- 计算每个方向的影响权重。在最基本的情况下,权重可以通过法线的绝对值来获得。例如,法线的X分量越大,X方向的纹理权重也就越大。
- 将三个方向的纹理进行混合:根据每个方向的位置和它的权重计算最终的颜色。
最终输出:
- 根据混合后的颜色输出最终的像素颜色。
uniform sampler2D textureX;
uniform sampler2D textureY;
uniform sampler2D textureZ;
uniform vec3 normal; // 片段法线
uniform vec3 position; // 片段位置
uniform float blendFactor; // 用于调节混合程度
vec4 triplanarMap(vec3 normal, vec3 position) {
vec3 absNormal = abs(normal);
// 纹理坐标
vec2 coordX = position.yz; // YZ平面
vec2 coordY = position.zx; // ZX平面
vec2 coordZ = position.xy; // XY平面
// 获取纹理
vec4 texX = texture(textureX, coordX);
vec4 texY = texture(textureY, coordY);
vec4 texZ = texture(textureZ, coordZ);
// 计算权重
float weightX = absNormal.x;
float weightY = absNormal.y;
float weightZ = absNormal.z;
// 归一化权重
float totalWeight = weightX + weightY + weightZ;
weightX /= totalWeight;
weightY /= totalWeight;
weightZ /= totalWeight;
// 混合纹理
vec4 finalColor = (texX * weightX) + (texY * weightY) + (texZ * weightZ);
return finalColor;
}