GLSL 片段着色器智能平铺纹理
GLSL Fragment Shader to intelligently tile texture
我正在创建一个游戏,其中我有不同大小的平台 - 但始终是 8 像素的乘法,并且始终是 8 像素高。
所有平台都使用与此处相同的纹理:
纹理分为三个 8x8 部分。我希望能够通过片段着色器 "tile" 此纹理,以便仅在左端应用前 8 个像素 - 中间 8 个像素的平铺像素与几何宽度减去最后 8 个像素一样多渲染 "end" 图块的像素。类似于这个宽 32 像素、高 8 像素的几何图形:
我使用的框架不允许设置顶点和 UV(只是一个简单的带纹理的四边形)所以我无法在几何体中修复它。
我在片段着色器中得到以下制服:
sampler2D
u_texture - 上面的纹理
vec2
u_sprite_size - 我想要平铺纹理的几何尺寸(以像素为单位)
.. 而这个变化:
- vec2 v_tex_coord - 用于访问纹理的坐标。这些被归一化,因此点 (0.0,0.0) 在
纹理的左下角。
可以通过代码添加其他制服。
谁能告诉我如何创建片段着色器?
您可能已经知道,实现此目的的典型方法是将 UV 坐标与顶点一起发送。数据。但为了安全起见,我会重申。为了进行纹理映射,需要将纹理坐标从顶点着色器传递到片段着色器以进行插值。由于您无法提供正确映射的 UV 坐标,因此您需要在片段着色器中即时计算它们,除非正确完成,否则这将是复杂且容易出错的。也就是说,请考虑归一化 UV 坐标的范围是 0-1,这样您的 8x8 图块就是实际的 {0-1}x{0-1}。对于随机几何体,您确实需要知道宽度、高度和世界偏移量才能成功自动生成 UV(无失真)。
另一方面,如果可能的话,你最好的办法是使用对象实例化,这样你就可以绘制任何相等 宽度和高度,以便您有四个已知点,您可以通过硬编码将其映射到 UV [0-1]x[0-1]。它们只是使用模型矩阵偏移量在行中实例化相同的图块。基本上它是克隆一个已知的正方形几何体,并尽可能地平铺相同的对象。将所有类型的正方形(左、中和右)视为一个单独的实例集合。那么新的问题将是生成一个接一个地偏移行中的图块的模型矩阵。
这也是一种生成大世界的非常有效的方法,传递给 GPU 的三角形相对较少。
我试图解决你的问题,所以我写了一个简单的片段着色器。它可能不是最干净的解决方案,因此您可以尝试对其进行优化。
您需要做的第一件事是计算对象中的图块数(仅垂直方向):
float verticalTiles = u_sprite_size.x / 8.0;
之后,您必须计算您当前正在处理的 tile 以及该图块的 UV。为此,您将 uv 坐标乘以垂直瓷砖的数量并将该值下限,因为您只需要整数。然后还需要对值进行分形得到uv坐标:
float tile = floor(verticalTiles * v_tex_coord.x);
float tileUV = fract(verticalTiles * v_tex_coord.x); // [0; 1] for each tile
现在您只想将纹理的前 8 个像素应用到第一个图块,将后 8 个像素应用到最后一个图块。其他图块将具有中间的 8 个像素。可以使用简单的 if 语句对其进行采样。我们只计算 U
值,因为 V
无论如何都不会改变:
float resultU;
if(tile == 0.0){
// If this is first tile, then we want the UV coordinates of the first 8 pixels.
// We divide tileUV by 3 to get UV coordinates [0; 0.3333].
resultU = tileUV / 3.0;
}else if(tile == verticalTiles - 1.0){
// If this is last tile we do the same thing but add two thirds
// to the coordinates to get last 8 pixels [0.6666; 1]
resultU = tileUV / 3.0 + (2.0 / 3.0);
}else{
// Else we want the middle 8 pixels [0.3333; 0.6666]
resultU = tileUV / 3.0 + (1.0 / 3.0);
}
然后使用这个值从纹理中采样:
gl_FragColor = texture2D( u_texture, vec2(resultU, v_tex_coord.y));
我正在创建一个游戏,其中我有不同大小的平台 - 但始终是 8 像素的乘法,并且始终是 8 像素高。
所有平台都使用与此处相同的纹理:
纹理分为三个 8x8 部分。我希望能够通过片段着色器 "tile" 此纹理,以便仅在左端应用前 8 个像素 - 中间 8 个像素的平铺像素与几何宽度减去最后 8 个像素一样多渲染 "end" 图块的像素。类似于这个宽 32 像素、高 8 像素的几何图形:
我使用的框架不允许设置顶点和 UV(只是一个简单的带纹理的四边形)所以我无法在几何体中修复它。
我在片段着色器中得到以下制服:
sampler2D u_texture - 上面的纹理
vec2 u_sprite_size - 我想要平铺纹理的几何尺寸(以像素为单位)
.. 而这个变化:
- vec2 v_tex_coord - 用于访问纹理的坐标。这些被归一化,因此点 (0.0,0.0) 在 纹理的左下角。
可以通过代码添加其他制服。
谁能告诉我如何创建片段着色器?
您可能已经知道,实现此目的的典型方法是将 UV 坐标与顶点一起发送。数据。但为了安全起见,我会重申。为了进行纹理映射,需要将纹理坐标从顶点着色器传递到片段着色器以进行插值。由于您无法提供正确映射的 UV 坐标,因此您需要在片段着色器中即时计算它们,除非正确完成,否则这将是复杂且容易出错的。也就是说,请考虑归一化 UV 坐标的范围是 0-1,这样您的 8x8 图块就是实际的 {0-1}x{0-1}。对于随机几何体,您确实需要知道宽度、高度和世界偏移量才能成功自动生成 UV(无失真)。
另一方面,如果可能的话,你最好的办法是使用对象实例化,这样你就可以绘制任何相等 宽度和高度,以便您有四个已知点,您可以通过硬编码将其映射到 UV [0-1]x[0-1]。它们只是使用模型矩阵偏移量在行中实例化相同的图块。基本上它是克隆一个已知的正方形几何体,并尽可能地平铺相同的对象。将所有类型的正方形(左、中和右)视为一个单独的实例集合。那么新的问题将是生成一个接一个地偏移行中的图块的模型矩阵。
这也是一种生成大世界的非常有效的方法,传递给 GPU 的三角形相对较少。
我试图解决你的问题,所以我写了一个简单的片段着色器。它可能不是最干净的解决方案,因此您可以尝试对其进行优化。
您需要做的第一件事是计算对象中的图块数(仅垂直方向):
float verticalTiles = u_sprite_size.x / 8.0;
之后,您必须计算您当前正在处理的 tile 以及该图块的 UV。为此,您将 uv 坐标乘以垂直瓷砖的数量并将该值下限,因为您只需要整数。然后还需要对值进行分形得到uv坐标:
float tile = floor(verticalTiles * v_tex_coord.x);
float tileUV = fract(verticalTiles * v_tex_coord.x); // [0; 1] for each tile
现在您只想将纹理的前 8 个像素应用到第一个图块,将后 8 个像素应用到最后一个图块。其他图块将具有中间的 8 个像素。可以使用简单的 if 语句对其进行采样。我们只计算 U
值,因为 V
无论如何都不会改变:
float resultU;
if(tile == 0.0){
// If this is first tile, then we want the UV coordinates of the first 8 pixels.
// We divide tileUV by 3 to get UV coordinates [0; 0.3333].
resultU = tileUV / 3.0;
}else if(tile == verticalTiles - 1.0){
// If this is last tile we do the same thing but add two thirds
// to the coordinates to get last 8 pixels [0.6666; 1]
resultU = tileUV / 3.0 + (2.0 / 3.0);
}else{
// Else we want the middle 8 pixels [0.3333; 0.6666]
resultU = tileUV / 3.0 + (1.0 / 3.0);
}
然后使用这个值从纹理中采样:
gl_FragColor = texture2D( u_texture, vec2(resultU, v_tex_coord.y));