2

I asked this question before about how to pass a data array to a fragment shader for coloring a terrain, and it was suggested I could use a texture's RGBA values.

I'm now stuck trying to work out how I would also use the yzw values. This is my fragment shader code:

vec4 data = texture2D(texture, vec2(verpos.x / 32.0, verpos.z / 32.0));
float blockID = data.x;
vec4 color;

if (blockID == 1.0) {
    color = vec4(0.28, 0.52, 0.05, 1.0);
}
else if (blockID == 2.0) {
    color = vec4(0.25, 0.46, 0.05, 1.0);
}
else if (blockID == 3.0) {
    color = vec4(0.27, 0.49, 0.05, 1.0);
}

gl_FragColor = color;

This works fine, however as you can see it's only using the float from the x-coordinate. If it was also using the yzw coordinates the texture size could be reduced to 16x16 instead of 32x32 (four times smaller).

The aim of this is to create a voxel-type terrain, where each 'block' is 1x1 in space coordinates and is colored based on the blockID. Looks like this: enter image description here

Outside of GLSL this would be simple, however with no ability to store which blocks have been computed I'm finding this difficult. No doubt, I'm over thinking things and it can be done with some simple math.


EDIT:
Code based on Wagner Patriota's answer:

vec2 pixel_of_target = vec2( verpos.xz * 32.0 - 0.5 ); // Assuming verpos.xz == uv_of_target ?

// For some reason mod() doesn't support integers so I have to convert it using int()
int X = int(mod(pixel_of_target.y, 2.0) * 2.0 + mod(pixel_of_target.x, 2.0));

// Gives the error "Index expression must be constant"
float blockID = data[ X ];

About the error, I asked a question about that before which actually led to me asking this one. :P
Any ideas? Thanks! :)

4

1 回答 1

2

这个想法是替换:

float blockID = data.x;

经过

float blockID = data[ X ];

WhereX是一个整数,可让您从 16x16 数据图像中选择 R、G、B 或 A。

问题是如何根据X你的紫外线计算?

好的,你有一个目标图像(32x32)和数据图像(16x16)。所以让我们这样做:

ivec pixel_of_target = ivec( uv_of_target * 32.0 - vec2( 0.5 ) ); // a trick!

将您的 UV 与纹理尺寸(在本例中为 32)相乘,您会找到确切的像素。-0.5 是必要的,因为您正在尝试“从纹理中查找像素”。当然,纹理在“像素中心”之间具有插值。您需要像素的确切中心...

pixel_of_target是一个ivec(整数),你可以准确地识别你在哪里画!所以现在的事情是确定(基于您正在绘制的像素)您应该从 16x16 纹理中获得哪个通道。

int X = ( pixel_of_target.y % 2 ) * 2 + pixel_of_target.x % 2;
float blockID = data[ X ]; // party on!

上面的这个表达式可以让你X根据目标像素选择正确的索引!在您的“数据纹理”16x16 上,将您的 (R,G,B,A) 映射到目标上每组 4 个像素的 (R,G,B,A) 到 (左上、右上、左下、右下) (或者可能是上-如果您愿意,请放下...您可以弄清楚)

更新:

因为您使用的是 WebGL,所以应该更改一些细节。我这样做了,它奏效了。

vec2 pixel_of_target = vTextureCoord * 32.0 + vec2( 0.5 ); // the signal changed!
int _x = int( pixel_of_target.x );
int _y = int( pixel_of_target.y );
int X = mod( _y, 2 ) * 2 + mod( _x, 2 );

我用这个来做我的测试:

if ( X == 0 )
    gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
else if ( X == 1 )
    gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );
else if ( X == 2 )
    gl_FragColor = vec4( 0.0, 0.0, 1.0, 1.0 );
else if ( X == 3 )
    gl_FragColor = vec4( 1.0, 0.0, 1.0, 1.0 );

我的图像工作得很好:

飞涨

在这里,我用 Photoshop 放大以查看像素的细节。

PS1:因为我对WebGL不熟悉,所以无法在Chrome中运行WebGL,我用Firefox试了下,也没有找到mod()函数……所以我做了:

int mod( int a, int b )
{
    return a - int( floor( float( a ) / float( b ) ) * float( b ) );
}

PS2:我不知道为什么我必须对 vec2( 0.5 ) 求和而不是减法。WebGL 有点不同。它可能有这种转变。我不知道......它只是工作。

于 2013-10-30T03:01:40.840 回答