我正在创建一个游戏,我有不同大小的平台 - 但总是乘以8像素,总是8像素高。
所有平台都使用与此处相同的纹理:
纹理分为三个8x8部分。我希望能够"平铺"这个纹理通过片段着色器使得前8个像素仅应用于左端 - 中间8个像素的瓦片,其数量与几何体的宽度相同,减去最后8个像素,其中"结束"瓷砖呈现。类似于这种几何形状的东西,宽32像素,高8像素:
我使用的框架不允许设置顶点和UV(只是一个简单的纹理四边形),所以我无法在几何体中修复它。
我在片段着色器中得到以下制服:
sampler2D u_texture - 上面的纹理
VEC2 u_sprite_size - 我想要平铺纹理的几何体的大小(以像素为单位)
..而且这种变化:
可以通过代码添加其他制服。
有人能指出我如何创建片段着色器的方向吗?
答案 0 :(得分:1)
我试图解决你的问题所以我写了一个简单的片段着色器。它可能不是最干净的解决方案,因此您可以尝试优化它。
您需要的第一件事是计算对象中的块数(仅垂直):
float verticalTiles = u_sprite_size.x / 8.0;
之后,您必须计算当前正在处理的图块以及此图块的 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));
答案 1 :(得分:0)
您可能已经知道,实现此目的的典型方法是发送UV坐标和顶点。数据。但为了安全起见,我将重申。为了使纹理映射发生,需要将纹理坐标从顶点着色器传递到片段着色器以进行插值。由于您无法提供正确映射的UV坐标,因此您需要在片段着色器中动态计算它们,除非正确完成,否则它会发生错综复杂且容易出错。也就是说,考虑到归一化的UV坐标范围为0-1,因此您的8x8磁贴实际为{0-1} x {0-1}。对于随机几何,您确实需要知道宽度,高度和世界偏移量才能成功自动生成UV(无失真)。
另一方面,,如果可能,您最好的选择是使用对象实例化,以便绘制任何相等宽度和高度的方块这样你就有四个已知的点可以硬代码映射到UV [0-1] x [0-1]。它们使用模型矩阵偏移在行中实例化相同的图块。基本上它克隆了一个已知的方形几何体,并尽可能地平铺同一个物体。将所有类型的正方形(左,中和右)视为单独的实例集合。然后,新问题将在于生成模型矩阵,这些矩阵一个接一个地抵消线条中的瓦片。
这也是生成大型世界的一种非常有效的方法,其中相对较少的三角形传递给GPU。