3D “纹理”基础知识和改变纹理
“纹理”存储在位图文件中(例如JPG图像),可用于为一组多边形着色。 我们将在讲座中研究这一点。 在本教程中,我们将研究实用性。 我将遵循Joey的教程,描述如何进行Jogl所需的更改。
本章由三个主要部分组成:
1.使用纹理的基础知识
2.纹理单元
3.动态纹理和改变纹理
1.描述了纹理坐标是什么,使用纹理纤维类别,并显示如何使用它来纹理三角形。
2.演示了多种纹理的使用。 还讨论了效率问题。
3.表明,在显示循环期间可以动态更改纹理或切换到完全不同的纹理。
使用纹理的基础知识
(1)纹理坐标( Texture coordinates)
三角形的数据: 每个顶点都有三个属性:顶点位置(x,y,z),顶点颜色(r,g,b),纹理坐标(s,t)。 总共每个顶点需要8个浮子。 所有数据都存储在一个数组中。
private float[] vertices = {
// position (3 floats), colour (3 floats), tex cords (2 floats)
0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.5f, 1.0f,
0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f
};
private int vertexStride = 8;
private int vertexXYZFloats = 3;
private int vertexColourFloats = 3;
private int vertexTexFloats = 2;
private int[] indices = {
0, 1, 2
};
这表明每个顶点现在都有三个属性,由8个浮子组成。每个顶点的最后两个值是纹理坐标s和t,每个值在0.0至1.0范围内包含范围。在GPU上的栅格化过程中,将顶点的纹理坐标值在三角形表面上插值(在顶点着色之后和片段着色之前发生)。使用片段着色器,然后在纹理图像中查找相关坐标以检索存储在那里的颜色 - 我们称此采样纹理。
引入了一个额外的类变量vertextexfloats,以描述纹理坐标是两个浮子。此变量用于FillBuffers()。要注意的主要内容是,每个顶点现在都有一个位置,颜色和纹理坐标,并且必须相应地计算数据列表中的“步幅”和偏移。值得注意的是,对于我们的示例,顶点属性是交错的(abcabcabc)。因此,步幅需要跨越连续的顶点属性集(ABC - 在我们的示例中,这是8个浮标)。对于以AAABBBCCC(即所有位置,然后所有颜色,然后所有纹理坐标)组织的数据结构,该过程将有所不同。
练习
尝试更改程序清单中的顶点的纹理坐标(在t01gleventlistener.java中)。 例如,对于顶点1,将纹理坐标从0.5F,1.0F更改为0.9F,0.7F。 然后将它们更改为1.2F,2.4F。 解释正在发生的事情。
解决方案:
悬挂顶点的纹理坐标意味着查找纹理图的不同部分。
根据三角形的形状,纹理可能在渲染三角形的表面上扭曲以及粘贴到它的纹理区域的形状,例如粘贴内容斜角三角上的等边三角形 - 必须拉伸信息。
当使用纹理坐标大于1.0时,可以根据某些OpenGL设置。 如果将纹理设置为重复(好像是在无限平面上铺有瓷砖),则使用较大的纹理坐标值显示出更好的效果
(2)加载和创建纹理
此类包含一种静态方法,可以从文件加载图像并创建OpenGL纹理,如下一个代码所示。 方法LoadTexture()可用于设置Joey教程中描述的一些参数。 确保您阅读Joey对可以为纹理设置的参数的描述。 此类的当前版本仅处理RGB .jpg图像,而不是RGBA图像。 其他方法可以写入加载其他文件类型 - 可以更改方法名称以指示已加载的文件类型。
import java.io.File;
import java.io.FileInputStream;
import com.jogamp.opengl.*;
import com.jogamp.opengl.util.texture.spi.JPEGImage;
public final class TextureLibrary {
// only deals with rgb jpg files
public static int[] loadTexture(GL3 gl, String filename) {
return loadTexture(gl, filename, GL.GL_REPEAT, GL.GL_REPEAT,
GL.GL_LINEAR, GL.GL_LINEAR);
}
public static int[] loadTexture(GL3 gl, String filename,
int wrappingS, int wrappingT, int filterS, int filterT) {
int[] textureId = new int[1];
try {
File f = new File(filename);
JPEGImage img = JPEGImage.read(new FileInputStream(f));
gl.glGenTextures(1, textureId, 0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId[0]);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, wrappingS);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, wrappingT);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, filterS);
gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, filterT);
gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, img.getWidth(),
img.getHeight(), 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img.getData());
gl.glGenerateMipmap(GL.GL_TEXTURE_2D);
/*gl.glTexParameteri(GL.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER,
GL2.GL_LINEAR_MIPMAP_LINEAR); */
gl.glBindTexture(GL.GL_TEXTURE_2D, 0);
}
catch(Exception e) {
System.out.println("Error loading texture " + filename);
}
return textureId;
}
}
initialise()方法在loadTexture()并将结果存储在类变量textureid1中。 我们将查看以后使用此变量的渲染方法。
private int[] textureId1 = new int[1];
public void initialise(GL3 gl) {
shader = new Shader(gl, "vs_T01.txt", "fs_T01.txt");
fillBuffers(gl);
textureId1 = TextureLibrary.loadTexture(gl, "wattBook.jpg");
}
(3)顶点和碎片着色器
下一个代码更改顶点着色器以接受顶点的纹理坐标(在属性位置2中),然后将坐标转发到栅格化过程,然后转到片段着色器。 下下一个代码,片段着色器使用AtexCoord接受栅格化过程中的纹理坐标。 剩下的就是使用纹理坐标查找纹理中的值并输出片段的颜色。 但是如何?
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;
out vec3 aColor;
out vec2 aTexCoord;
void main() {
gl_Position = vec4(position.x, position.y, position.z, 1.0);
aColor = color;
aTexCoord = texCoord;
}
#version 330 core
in vec3 aColor;
in vec2 aTexCoord;
out vec4 fragColor;
uniform sampler2D first_texture;
void main() {
// fragColor = vec4(aColor, 1.0f);
fragColor = vec4(texture(first_texture, aTexCoord).rgb, 1.0f);
// fragColor = vec4(texture(first_texture, aTexCoord).rgb * aColor, 1.0f);
}
GLSL具有用于纹理对象的内置数据类型,称为采样器。后缀说明它是什么类型的纹理,在我们的情况下是Sampler2d,即2D纹理。纹理被声明为统一,因为该片段着色器的所有实例都将使用相同的纹理,即,对于所有实例,纹理都是相同的常数。由于它是统一的,我们需要从主要应用程序中传递一个值,就像我们为以前的示例中的程序中所做的那样。我们将尽快介绍如何更详细地传递纹理数据。
在片段着色器中,使用GLSL函数纹理()。这是使用纹理坐标(AtexCoord)在纹理(FIRST_TEXTURE)中采样颜色,并始终返回VEC4(即RGBA颜色),其中组件值在范围内[0,1]。
值得对片段着色器中的代码进行简要评论。纹理函数从纹理单元返回值。由于我们存储在此中的图像是仅包含RGB数据的JPG图像,因此我们需要小心使用它。因此,.RGB用于访问此数据。我们知道Acolor是VEC3, 因此,可以将两组值乘在一起。片段着色器的输出颜色(Fragcolor)是VEC4。我们可以通过将上一个乘法的结果作为VEC4的前三个组件来构建此问题,然后用1.0F作为第四个组件。
(4)绑定纹理
专注于t01_gleventlistener.java中的方法Render()
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
shader.use(gl);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
// good practice to set everything back to defaults.
gl.glActiveTexture(GL.GL_TEXTURE0);
}
在这里,我们声明gl_texture0应该是活动纹理单元,我们使用变量textureID1和呼叫glbindTexture()将其绑定到纹理。 这类似于在我们绘制一组顶点之前绑定相关的顶点阵列。
那么,主计划和统一着色器中的统一之间的联系在哪里? 不是。 有一个假设是GL_TEXTURE0是默认的活动纹理单元,并且如果在片段着色器中使用sampler2d,它将自动涉及此。 因此,片段着色器中的统一不是从主程序中明确设置的。 有人会说这是草率的编程,因为并非所有GPU的驱动程序都会做出这一假设。 一些GPU驱动程序可能会失败。 为了安全起见,应通过添加以下代码明确设置制服:
gl.glUniform1i(gl.glGetUniformLocation(shader.getID(), "first_texture"), 0);
仅在阴暗之后添加到渲染方法中。使用(gl)。 实际上,我已经在班级着色器中添加了一种方法,因此可以写为:
shader.setInt(gl, "first_texture", 0);
该代码在片段着色器中设置统一变量first_texture,以参考纹理单元0。我们将在下一部分中看到它的使用。
练习
在t01_gleventlistener.java中,method inationalise()使用’wattbook.jpg’作为文件名。 尝试从Internet下载JPG图像,然后使用此图像。 (或者,在油漆工具中打开“ wattbook.jpg”并对它进行一些编辑。然后使用程序中的编辑版本。)请注意,具有X和Y尺寸的图像均为2的功率2,例如 256x256是优选的
2.纹理单元
OpenGL至少具有至少16个纹理单元:gl_texture0至gl_texture15。 程序T02使用其中两个。每个顶点都设置为不同的颜色(红色,绿色和蓝色),并且将两个纹理混合在“ wattbook.jpg”和“ chequerboard.jpg”中。 程序在t02_gleventlistener.java中给出初始()和render()方法。
分别创建两个纹理并存储在变量textureID1和textureId2中。 在Render()中,这些势必为纹理单元0和1(使用Shader.SetInt()),它们与first_texture和second_texture关联的片段着色器中的section sextule。
private int[] textureId1 = new int[1];
private int[] textureId2 = new int[1];
public void initialise(GL3 gl) {
shader = new Shader(gl, "vs_T02.txt", "fs_T02.txt");
fillBuffers(gl);
textureId1 = TextureLibrary.loadTexture(gl, "wattBook.jpg");
textureId2 = TextureLibrary.loadTexture(gl, "chequerboard.jpg");
}
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
shader.use(gl);
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
gl.glActiveTexture(GL.GL_TEXTURE0);
}
Fragment shader
#version 330 core
in vec3 aColor;
in vec2 aTexCoord;
out vec4 fragColor;
uniform sampler2D first_texture;
uniform sampler2D second_texture;
void main() {
// fragColor = vec4(aColor, 1.0f);
// fragColor = vec4(texture(first_texture, aTexCoord).rgb, 1.0f);
// fragColor = vec4(texture(first_texture, aTexCoord).rgb * aColor, 1.0f);
// fragColor = vec4(texture(second_texture, aTexCoord).rgb * aColor, 1.0f);
fragColor = vec4(mix(texture(first_texture, aTexCoord),
texture(second_texture, aTexCoord), 0.3f).rgb
* aColor, 1.0f);
}
这两个纹理使用称为Mix的GLSL函数组合,该功能以参数为两种颜色和浮点值。 第一种颜色来自first_texture,第二颜色来自second_texture。 值为0.3的值返回第一颜色的70%的混合物和第二种颜色的30%。 然后将混合功能的结果乘以片段的输入颜色(这是由栅格化阶段输出的,该阶段从各个三角形的顶点插值值)。
练习
1.尝试其他一些评论片段着色器中的线条。 在运行程序之前,每次预测效果。添加一个均匀的变量,该变量随着时间的推移而变化。
解决方案:
碎片着色器添加了一个均匀的MixAmount:
#version 330 core
in vec3 aColor;
in vec2 aTexCoord;
out vec4 fragColor;
uniform float mixAmount; // ***
uniform sampler2D first_texture;
uniform sampler2D second_texture;
void main() {
fragColor = vec4(mix(texture(first_texture, aTexCoord),
texture(second_texture, aTexCoord), mixAmount).rgb // ***
* aColor, 1.0f);
}
然后,渲染方法使用Math.sin函数和系统时间来改变MixAmount:
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
double elapsedTime = getSeconds() - startTime; // ***
shader.use(gl);
shader.setFloat(gl, "mixAmount", (float)((Math.sin(elapsedTime)+1)*0.5)); // ***
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
// always good practice to set everything back to defaults once configured.
gl.glActiveTexture(GL.GL_TEXTURE0);
}
2.添加第三个纹理,并使用混合的多个调用来混合三个纹理。
解决方案:
碎片着色器添加了一个均匀的MixAmount…
初始化和渲染方法需要更新以提供第三种纹理:
// ***************************************************
/* THE SCENE
* Now define all the methods to handle the scene.
*/
private int[] textureId1 = new int[1];
private int[] textureId2 = new int[1];
private int[] textureId3 = new int[1];
public void initialise(GL3 gl) {
shader = new Shader(gl, "vs_T02.txt", "fs_T02e3.txt");
fillBuffers(gl);
textureId1 = TextureLibrary.loadTexture(gl, "wattBook.jpg");
textureId2 = TextureLibrary.loadTexture(gl, "chequerboard.jpg");
textureId3 = TextureLibrary.loadTexture(gl, "cloud.jpg");
}
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
double elapsedTime = getSeconds() - startTime;
shader.use(gl);
shader.setFloat(gl, "mixAmount", (float)((Math.sin(elapsedTime)+1)*0.5));
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
shader.setInt(gl, "third_texture", 2);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId3[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
// always good practice to set everything back to defaults once configured.
gl.glActiveTexture(GL.GL_TEXTURE0);
}
(1)纹理结合和效率
该程序几乎与t02.java相同。唯一的更改显示在下一个代码中。在加载文件中加载纹理后,在方法初始()中完成了使纹理单元活动并将特定纹理绑定到纹理单元的调用。方法渲染()现在仅将片段着色器中相关统一的名称分配给每个纹理单元。这比不断重新固定纹理单元更有效。
我们可以以这种方式加载纹理的集合,并将它们存储在与纹理单元相关的纹理库中。然后,在主渲染循环中,我们将分配我们希望参考的特定统一采样器2D的纹理单元。当然,如果我们比纹理单元具有更多的纹理,那么我们将需要对渲染循环中的活动纹理进行一些重构和切换。但是,在练习表的示例中,我们不需要超过16个纹理。
private int[] textureId1 = new int[1];
private int[] textureId2 = new int[1];
public void initialise(GL3 gl) {
shader = new Shader(gl, "vs_T03.txt", "fs_T03.txt");
fillBuffers(gl);
// This will not work if the loading process is interspersed with making the textures active
// Thus, the textures are all loaded first.
textureId1 = TextureLibrary.loadTexture(gl, "wattBook.jpg");
textureId2 = TextureLibrary.loadTexture(gl, "chequerboard.jpg");
// bind the two textures to the first two texture units
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
}
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
shader.use(gl);
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
}
我们的计划中还可以考虑其他效率。 例如,从GPU获得统一的位置相对较慢。 取而代之的是,可以在初始方法中查询统一的位置并存储在全局变量中。 然后,在渲染方法中,他们只需要设置为特定值。 另外,如果一个统一不会从一个渲染呼叫到下一个渲染呼叫变化,则无需继续重置其值。 该制服将在着色器程序中设置后保留其值 - 它被认为是活跃的,直到重新链接着色器程序为止。 我们不必担心这些额外的效率,而只使用我们在着色器类中写的方法来设置渲染环内部的命名制服的值。 当我们学习OpenGL时,这使代码更清晰。
3.动态纹理和改变纹理
该程序中使用了两种纹理。 一种纹理是静态的,而另一个纹理不断滚动在另一个纹理的顶部.该程序在顶点着色器中使用统一来改变纹理的纹理坐标。
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
double elapsedTime = getSeconds() - startTime;
shader.use(gl);
double t = elapsedTime*0.1;
float offsetX = (float)(t - Math.floor(t));
float offsetY = 0.0f;
shader.setFloat(gl, "offset", offsetX, offsetY);
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
gl.glActiveTexture(GL.GL_TEXTURE0);
}
每次调用render(),都会更新顶点着色器中的统一偏移量。 该值被添加到TexCoord中以产生MoveTexCoord,然后将其传递给Rasterizer,该横幅器将每个顶点的值插入三角形上,产生一组片段,每个片段都具有颜色(Acolor)和两组纹理坐标(AtexCoord(AtexCoord) 和MovingTexCoord)。 AtexCoord在片段着色器中用于示例first_texture,而MovionTexCoord则在片段着色器中用于示例second_texture。 使用GLSL混合功能将结果混合在一起。
替代解决方案将均匀的偏移量放在碎片着色器而不是顶点着色器中。 然后将偏移添加到插值的AtexCoord值中。
Vertex shader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;
out vec3 aColor;
out vec2 aTexCoord;
out vec2 movingTexCoord;
uniform vec2 offset;
void main() {
gl_Position = vec4(position.x, position.y, position.z, 1.0);
aColor = color;
aTexCoord = texCoord;
movingTexCoord = texCoord + offset;
}
Fragment shader
#version 330 core
in vec3 aColor; // not needed
in vec2 aTexCoord;
in vec2 movingTexCoord;
out vec4 fragColor;
uniform sampler2D first_texture;
uniform sampler2D second_texture;
void main() {
fragColor = vec4(mix(texture(first_texture, aTexCoord),
texture(second_texture, movingTexCoord),
0.5f).rgb, 1.0f);
}
练习
更改Method Render()中偏移的计算,以查看您可以创建什么动态效果。
解决方案:
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
double elapsedTime = getSeconds() - startTime;
shader.use(gl);
double t = elapsedTime*0.1; // *0.1 slows it down a bit
float offsetX = (float)(t - Math.floor(t));
float offsetY = (float)(Math.sin(elapsedTime)*0.1);
shader.setFloat(gl, "offset1", offsetX, offsetY);
offsetX = (float)Math.sin((t - Math.floor(t))*1.5708);
offsetY = (float)(Math.sin(elapsedTime*1.1)*0.2);
shader.setFloat(gl, "offset2", offsetX, offsetY);
float temp = (float)Math.sin((t - Math.floor(t))*1.5708);
offsetX = temp*temp;
offsetY = (float)(Math.sin(elapsedTime*0.9)*0.05);
shader.setFloat(gl, "offset3", offsetX, offsetY);
shader.setInt(gl, "first_texture", 0);
shader.setInt(gl, "second_texture", 1);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
// always good practice to set everything back to defaults once configured.
gl.glActiveTexture(GL.GL_TEXTURE0);
}
The vertex shader
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;
out vec3 aColor;
out vec2 aTexCoord;
out vec2 movingTexCoord1, movingTexCoord2, movingTexCoord3;
uniform vec2 offset1, offset2, offset3;
void main() {
gl_Position = vec4(position.x, position.y, position.z, 1.0);
aColor = color;
aTexCoord = texCoord;
movingTexCoord1 = texCoord + offset1;
movingTexCoord2 = texCoord + offset2;
movingTexCoord3 = texCoord + offset3;
}
The fragment shader
#version 330 core
in vec3 aColor; // not needed
in vec2 aTexCoord;
in vec2 movingTexCoord1, movingTexCoord2, movingTexCoord3;
out vec4 fragColor;
uniform sampler2D first_texture;
uniform sampler2D second_texture;
void main() {
vec4 temp = mix(texture(first_texture, aTexCoord),
texture(second_texture, movingTexCoord1),
0.5f);
temp = mix(temp, texture(second_texture, movingTexCoord2), 0.3f);
temp = mix(temp, texture(second_texture, movingTexCoord3), 0.1f);
fragColor = vec4(temp.rgb, 1.0f);
}
最终程序将4个纹理加载到内存中,然后在同一三角形上依次显示其中的每一个。
Methods initialise() and render() in T05_GLEventListener.java
private int[] textureId1 = new int[1];
private int[] textureId2 = new int[1];
private int[] textureId3 = new int[1];
private int[] textureId4 = new int[1];
private final int NUM_TEXTURES = 4;
public void initialise(GL3 gl) {
shader = new Shader(gl, "vs_T05.txt", "fs_T05.txt");
fillBuffers(gl);
textureId1 = TextureLibrary.loadTexture(gl, "paintstrokes/wattBook.jpg");
textureId2 = TextureLibrary.loadTexture(gl, "paintstrokes/wattBook1.jpg");
textureId3 = TextureLibrary.loadTexture(gl, "paintstrokes/wattBook2.jpg");
textureId4 = TextureLibrary.loadTexture(gl, "paintstrokes/wattBook3.jpg");
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId1[0]);
gl.glActiveTexture(GL.GL_TEXTURE1);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId2[0]);
gl.glActiveTexture(GL.GL_TEXTURE2);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId3[0]);
gl.glActiveTexture(GL.GL_TEXTURE3);
gl.glBindTexture(GL.GL_TEXTURE_2D, textureId4[0]);
startTime = getSeconds();
}
public void render(GL3 gl) {
gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
double elapsedTime = getSeconds() - startTime;
int currentTexture
= (int)Math.floor(2*(1+Math.sin(elapsedTime+Math.toRadians(270))));
shader.use(gl);
shader.setInt(gl, "first_texture", currentTexture);
gl.glBindVertexArray(vertexArrayId[0]);
gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);
gl.glBindVertexArray(0);
}