【GLSL教程】(八)纹理贴图

简单的纹理贴图(Simple Texture)
为了在GLSL中应用纹理,我们需要访问每个顶点的纹理坐标。GLSL中提供了一些属性变量,每个纹理单元一个:
attribute vec4 gl_MultiTexCoord0;
attribute vec4 gl_MultiTexCoord1;
attribute vec4 gl_MultiTexCoord2;
attribute vec4 gl_MultiTexCoord3;
attribute vec4 gl_MultiTexCoord4;
attribute vec4 gl_MultiTexCoord5;
attribute vec4 gl_MultiTexCoord6;
attribute vec4 gl_MultiTexCoord7;
GLSL还为访问每个纹理的纹理矩阵提供了一个一致变量数组:
uniform mat4 gl_TextureMatrix[gl_MaxTextureCoords];
顶点shader可以通过上面所示的内容访问OpenGL程序中指定的纹理坐标。然后必须为每个顶点计算纹理坐标,并保存在预先定义的易变变量gl_TexCoord[i]中,i表示纹理单元号。
下面这条语句直接复制OpenGL程序中指定的纹理坐标,作为纹理单元0的顶点纹理坐标。
gl_TexCoord[0]  = gl_MultiTexCoord0;
下面是个简单的例子,在顶点shader中设置纹理单元0的纹理坐标。
void main()
{
    gl_TexCoord[0] = gl_MultiTexCoord0;
    gl_Position = ftransform();
}
如果你想使用纹理矩阵,可以这样操作:
void main()
{
    gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
    gl_Position = ftransform();
}
前面说过,gl_TexCoord是一个易变变量,所以在片断shder中可以访问经过插值的纹理坐标。
为了访问纹理的数值,在片断shader中有必要声明一个特殊的变量,对一个2D纹理可以可以这样写:
uniform sampler2D tex;
如果是1D或者3D的纹理,可以改成sampler1D和sampler3D。
这个用户定义的变量tex包含我们将会使用的纹理单元,通过texture2D函数我们可以得到一个纹素(texel),这是一个纹理图片中的像素。函数参数分别为simpler2D以及纹理坐标:
vec4 texture2D(sampler2D, vec2);
函数的返回值已经考虑了所有在OpenGL程序中定义的纹理设置,比如过滤、mipmap、clamp等。
我们的片断shader可以写成如下形式:
uniform sampler2D tex;

void main()
{
    vec4 color = texture2D(tex,gl_TexCoord[0].st);
    gl_FragColor = color;
}
注意访问gl_TexCoord时选择子st的使用。在本教程前面关于数据类型和变量的讨论中说过,访问纹理坐标时可以使用如下选择子:s、t、p、q。(r因为和rgb选择子冲突而没有使用)

本节内容Shader Designer的工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureSimple.zip

组合纹理与片断

OpenGL允许我们通过多种方式将纹理颜色和片断颜色联合到一起。下表显示了RGBA模式时可用的联合方式:

GL_REPLACEC = CtA = At
GL_MODULATEC = Ct*Cf A = At*Af
GL_DECALC = Cf * (1 – At) + Ct * AtA = Af
表中Ct和At表示纹理的颜色和alpha值,Cf和Af表示片断(fragment)的颜色和alpha值,C和A表示最终的颜色和alpha值。
上一节的例子就相当于使用了GL_REPLACE模式。下面我们我们准备在一个立方体上实现与GL_MODULATE等同的效果。两个shader只计算使用一个白色方向光的散射以及环境光成分,关于材质的完整定义请参照光照有关的章节。
因为使用了光照,所以顶点shader中必须处理法线信息。必须将法线变换到视图空间然后归一化,光线方向向量也必须归一化(光线方向向量已经由OpenGL变换到了视图空间)。现在新的顶点shader如下:

varying vec3 lightDir,normal;

void main()
{
    normal = normalize(gl_NormalMatrix * gl_Normal);

    lightDir = normalize(vec3(gl_LightSource[0].position));
    gl_TexCoord[0] = gl_MultiTexCoord0;

    gl_Position = ftransform();
}
在片断shader中,光照得到的片断的颜色和alpha值在cf和af中分别计算。shader中剩余代码按照GL_MODULATE的公式计算:
varying vec3 lightDir,normal;
uniform sampler2D tex;

void main()
{
    vec3 ct,cf;
    vec4 texel;
    float intensity,at,af;

    intensity = max(dot(lightDir,normalize(normal)),0.0);
    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                  gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st);
    ct = texel.rgb;
    at = texel.a;

    gl_FragColor = vec4(ct * cf, at * af);
}

Shader Designer的工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureComb.zip

多重纹理
在GLSL中实现多重纹理十分容易,我们只需要访问所有纹理即可。因为我们打算给每个纹理使用相同的纹理坐标,所以顶点shader不需要改动。片断shader中只需要进行些许改动,加上多个纹理的颜色值。
varying vec3 lightDir,normal;
uniform sampler2D tex;

void main()
{
    vec3 ct,cf;
    vec4 texel;
    float intensity,at,af;

    intensity = max(dot(lightDir,normalize(normal)),0.0);
    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                  gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st) +
          texture2D(l3d,gl_TexCoord[0].st);
    ct = texel.rgb;
    at = texel.a;

    gl_FragColor = vec4(ct * cf, at * af);
}
效果如下:

下面添加点不同的效果:在黑暗中发光。我们希望第二个纹理能在黑暗中发光,在没有光照时达到最亮,在有光照时变暗。

我们通过两步计算最终的颜色:首先将第一个纹理与片断颜色进行modulate计算,然后根据光照强度(indensity)加上第二个纹理单元。
如果indensity是0,第二个纹理单元取最大值,如果indensity为1,只取第二个纹理单元颜色的10%,当indensity在0和1之间时按这两个大小进行插值。可以使用smoothstep函数实现这个要求:
genType smoothStep(genType edge0, genType edge1, genType x);
如果x <= edge0结果是0,如果x >= edge1结果为1,如果edge0 < x < edge1结果在0和1之间进行Hermite插值。在本例中我们按如下方式调用:
coef = smoothStep(1.0, 0.2, intensity);
下面的片断shader实现了需要的效果:
varying vec3 lightDir,normal;
uniform sampler2D tex,l3d;

void main()
{
    vec3 ct,cf,c;
    vec4 texel;
    float intensity,at,af,a;

    intensity = max(dot(lightDir,normalize(normal)),0.0);

    cf = intensity * (gl_FrontMaterial.diffuse).rgb +
                      gl_FrontMaterial.ambient.rgb;
    af = gl_FrontMaterial.diffuse.a;

    texel = texture2D(tex,gl_TexCoord[0].st);

    ct = texel.rgb;
    at = texel.a;

    c = cf * ct;
    a = af * at;

    float coef = smoothstep(1.0,0.2,intensity);
    c += coef *  vec3(texture2D(l3d,gl_TexCoord[0].st));

    gl_FragColor = vec4(c, a);
}
Shader Designer的工程下载地址:
http://www.lighthouse3d.com/wp-content/uploads/2011/03/textureGlow.zip


  • 5
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
GLSL是OpenGL着色器语言,可以用于编写GPU计算程序,也就是GPGPU。以下是一个简单的GLSL GPGPU程序示例。 首先,我们需要一个着色器程序,它将执行我们的计算。以下是一个使用GLSL的简单计算着色器程序示例: ``` #version 330 layout (local_size_x = 16, local_size_y = 16) in; layout (rgba32f, binding = 0) uniform image2D inputImage; layout (rgba32f, binding = 1) uniform image2D outputImage; void main() { ivec2 storePos = ivec2(gl_GlobalInvocationID.xy); vec4 data = imageLoad(inputImage, storePos); // perform computation on data imageStore(outputImage, storePos, data); } ``` 这个着色器程序将从输入图像中读取像素数据并执行计算,然后将计算结果写回到输出图像中。 接下来,我们需要在主程序中设置输入和输出图像以及调用着色器程序。以下是一个示例程序: ``` #include <GL/glew.h> #include <GLFW/glfw3.h> #include <iostream> #include <vector> const int WIDTH = 512; const int HEIGHT = 512; int main() { if (!glfwInit()) { std::cerr << "Failed to initialize GLFW." << std::endl; return -1; } glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "GLSL GPGPU Tutorial", nullptr, nullptr); if (!window) { std::cerr << "Failed to create GLFW window." << std::endl; glfwTerminate(); return -1; } glfwMakeContextCurrent(window); glewExperimental = true; if (glewInit() != GLEW_OK) { std::cerr << "Failed to initialize GLEW." << std::endl; glfwTerminate(); return -1; } GLuint inputImage, outputImage; glGenTextures(1, &inputImage); glBindTexture(GL_TEXTURE_2D, inputImage); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, WIDTH, HEIGHT); glGenTextures(1, &outputImage); glBindTexture(GL_TEXTURE_2D, outputImage); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA32F, WIDTH, HEIGHT); GLuint shader = glCreateShader(GL_COMPUTE_SHADER); const char* shaderSource = "#version 330\n" "layout (local_size_x = 16, local_size_y = 16) in;\n" "layout (rgba32f, binding = 0) uniform image2D inputImage;\n" "layout (rgba32f, binding = 1) uniform image2D outputImage;\n" "void main() {\n" " ivec2 storePos = ivec2(gl_GlobalInvocationID.xy);\n" " vec4 data = imageLoad(inputImage, storePos);\n" " // perform computation on data\n" " imageStore(outputImage, storePos, data);\n" "}"; glShaderSource(shader, 1, &shaderSource, nullptr); glCompileShader(shader); GLuint program = glCreateProgram(); glAttachShader(program, shader); glLinkProgram(program); glUseProgram(program); glBindImageTexture(0, inputImage, 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); glBindImageTexture(1, outputImage, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); glDispatchCompute(WIDTH / 16, HEIGHT / 16, 1); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glfwTerminate(); return 0; } ``` 此程序将创建一个窗口,创建两个纹理,一个输入,一个输出,然后在主程序中调用着色器程序,将输入图像传递给着色器程序并从输出图像中读取结果。 以上是一个简单的GLSL GPGPU程序示例,你可以根据自己的需求进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值