OpenTK中文教程——1.6多种纹理

326 阅读3分钟

多种纹理和纹理单元

你可能想知道为什么在我们甚至没有用 GL.Uniform* 给它赋值的情况下,sampler2D 变量仍然是一个 uniform。实际上,我们可以使用 GL.Uniform1 给纹理采样器分配一个位置值,这样我们就可以在一个片段着色器中同时设置多个纹理。这个纹理的位置更常被称为纹理单元。纹理的默认纹理单元是 0,这也是默认激活的纹理单元,所以在前一节中我们不需要分配位置;请注意,并不是所有的图形驱动程序都会分配默认的纹理单元,所以前一节可能没有为你渲染。

纹理单元的主要目的是允许我们在着色器中使用多个纹理。通过将纹理单元分配给采样器,我们可以在激活相应的纹理单元后同时绑定到多个纹理。就像使用 GL.BindTexture 一样,我们可以使用 GL.ActiveTexture 激活纹理单元,传递我们想要使用的纹理单元:

GL.ActiveTexture(TextureUnit.Texture0); // 在绑定纹理之前先激活纹理单元
GL.BindTexture(TextureTarget.Texture2D, texture);

激活一个纹理单元后,后续的GL.BindTexture调用将把该纹理绑定到当前激活的纹理单元。纹理单元Texture0总是默认激活的,所以在前面的例子中使用GL.BindTexture时我们不需要激活任何纹理单元。

OpenGL 至少应该有 16 个纹理单元供您使用,您可以使用 Texture0Texture15 来激活它们。

我们仍然需要编辑片段着色器以接受另一个采样器。这现在应该相对简单:

#version 330 core

/* ... */

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

最终输出颜色现在是两个纹理查找的组合。GLSL 内置的 mix 函数接受两个值作为输入,并根据其第三个参数在这两个值之间进行线性插值。如果第三个值是 0.0,则返回第一个输入;如果是 1.0,则返回第二个输入值。一个 0.2 的值将返回第一个输入颜色的 80% 和第二个输入颜色的 20%,从而产生我们两个纹理的混合。

我们现在希望加载并创建另一个纹理;你应该对这些步骤很熟悉了。确保创建另一个纹理对象,加载图像并使用 GL.TexImage2D 生成最终纹理。对于第二个纹理,我们将使用一张你在学习 OpenGL 时的表情图片:你在学习OpenGL时的表情

要使用第二个纹理(以及第一个纹理),我们不得不稍微改变渲染过程,通过将两个纹理绑定到相应的纹理单元。首先,进入 Texture.cs,并修改 Use 函数如下:

void Use(TextureUnit unit = TextureUnit.Texture0)
{
    GL.ActiveTexture(unit);
    GL.BindTexture(TextureTarget.Texture2D, Handle);
}

然后,添加一个新属性来保存新纹理,并在 OnLoad 中加载它。

Texture texture2;

...

texture2 = new Texture("awesomeface.png");

新的纹理已可用,现在我们必须设置着色器的uniform。在Shader.cs中添加以下函数:

public void SetInt(string name, int value)
{
    int location = GL.GetUniformLocation(Handle, name);

    GL.Uniform1(location, value);
}

这个函数只是简化了设置着色器统一变量的过程。采样器在CPU中表示为整数;将统一变量设置为整数,它会检查纹理单元。

OnLoad中创建纹理后立即添加以下几行:

shader.SetInt("texture1", 0);
shader.SetInt("texture2", 1);

这将uniform texture1 设置为使用纹理单元0中的内容,而 texture2 使用纹理单元1中的内容。

最后,将 OnRenderFrame 修改为以下内容:

GL.Clear(ClearBufferMask.ColorBufferBit);
GL.BindVertexArray(VertexArrayObject);

texture.Use(TextureUnit.Texture0);
texture2.Use(TextureUnit.Texture1);
shader.Use();

GL.DrawElements(PrimitiveType.Triangles, indices.Length, DrawElementsType.UnsignedInt, 0);

Context.SwapBuffers();

base.OnRenderFrame(e);

现在你应该看到以下内容:纹理已绑定且着色器设置正确。

image.png