本章将更详细地查看着色器,包括如何将数据直接传输到顶点和片段着色器中的CPU-以前部分中的示例将数据用作字面价值(以及使用vertex)将硬编码的数据作为文字值(以及使用Vertex)来自GPU上顶点缓冲区的数据。
本章由三个主要部分组成:
1.着色器的基础知识
2.动态更改VBO数据
3.着色器类
着色器的基础知识处理直接将数据从CPU传输到GPU着色器。例如,我们可能希望在CPU上运行的程序的控制下更改三角形的颜色,或更改所绘制三角形的顶点的位置。
动态更改VBO数据的重点是如何动态地更改CPU程序循环中GPU上存储在GPU上的一些顶点数据。我们可以更改CPU上的一些顶点数据,然后在显示循环中绘制的每个帧中将所有顶点数据从CPU发送到GPU。但是,这可能非常昂贵,具体取决于顶点的数量。相反,在CPU上运行的程序可以将数据传输到GPU上的缓冲区一次,然后作为显示循环的一部分,操纵单个顶点数据,该数据已经存储在GPU上的缓冲液(或缓冲区)中,然后使用它在使用它呈现之前使用它。着色器。
着色器类将着色器代码分为单独的类。这简化了主要程序列表,因为可以在每个后续程序中使用相同的着色器类。在后面的部分中,我们将随着程序的复杂性的增加而重新访问着色器类,我们需要多个不同的着色器来为程序。
1.着色器的基础知识
这里我们讨论了可以从CPU转移到GPU以及GPU上的着色器之间的数据的种类。
3.1.1 ins and out(属性)
该程序类似于我们在以前查看的程序,我们仍在包含与OpenGL一起在一个类中的所有内容,并且我们仍在使用EBO,但仅使用一个三角形。 但是,现在我们准备好“陷入困境”,并向GPU上的着色器上的阴暗提供更多详细说明。
import java.nio.*;
import com.jogamp.common.nio.*;
import com.jogamp.opengl.*;
import com.jogamp.opengl.util.*;
import com.jogamp.opengl.util.awt.*;
import com.jogamp.opengl.util.glsl.*;public class S01_GLEventListener implements GLEventListener {public S01_GLEventListener() {}// ***************************************************/** METHODS DEFINED BY GLEventListener*/public void init(GLAutoDrawable drawable) {}public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {}public void display(GLAutoDrawable drawable) {}public void dispose(GLAutoDrawable drawable) {}// *************************************************** /* THE SCENE */// ***************************************************/* THE DATA */// ***************************************************/* THE BUFFERS */// ***************************************************/* THE SHADER */}
接下来的代码显示我们将考虑的着色器。 这些存储为字符串变量。 稍后,我们将看到如何从文本文件中加载它们,这将产生更灵活的过程,因为这些过程可以在任何文本编辑器中进行编辑。
// ***************************************************/* THE SHADER*/private String vertexShaderSource = "#version 330 core\n" +"\n" +"layout (location = 0) in vec3 position;\n" +"\n" +"out vec4 aColor;\n" + // colour is passed to rasterisation process"\n" +"void main() {\n" +" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n" +" aColor = vec4(0.55f, 0.0f, 0.55f, 1.0f);\n" + // vertex colour is set here// - a shade of magenta"}";private String fragmentShaderSource = "#version 330 core\n" +"\n" +"out vec4 fragColor;\n" +"\n" +"in vec4 aColor;\n" + // colour received from rasterisation process"\n" +"void main() {\n" +" fragColor = aColor;\n" + // output colour for fragment set to input colour"}";
GPU管道采用了描述三角形的顶点的顺序,用顶点着色器的单独实例操纵每个顶点(全部并行运行,可用资源),然后将三个顶点组的组隔开,以产生三角形的组,以产生三角形的组合。三角形的片段。栅格化意味着三角形顶点的值在三角形上双重插值,以产生每个片段的值。例如,对顶点位置进行双线插值,以产生每个片段的屏幕位置。
在以前的程序中,片段的颜色设置为片段着色器中的特定(红色,绿色,蓝色,alpha)值。由于每个片段都将三角形的每个片段都放入相同的片段着色器中,因此三角形中的每个像素都是相同的颜色。在程序清单3.2中,颜色是在顶点着色器中设置的。因此,它受GPU的栅栏化过程的约束。在这种情况下,由于每个顶点都设置为相同的颜色,因此栅格化过程将为每个片段产生相同的颜色!我们需要一种将每个顶点设置为不同颜色的方法。
考虑程序清单中的着色器属性。关键字表示数据(属性)正在转移到顶点着色器或片段着色器中。关键字表明数据(属性)正在转移到GPU管道中的下一个阶段。线#version 330核心表示正在使用默认的核心GLSL(GL阴影语言)版本3.30。
在GLSL中,定义顶点属性位置在GPU上的首选方法是使用布局(位置= x)。因此,以下行指出,顶点着色器期望在位置0中定义顶点位置: layout (location = 0) in vec3 position
可以选择任何变量名称。 我使用位置,因为它指示了变量包含的内容。 我将在每个程序中使用相同的名称。 在顶点着色器的主体内,可变GLPosition(类型VEC4)设置为一个值。 这是GPU上少数预定义变量之一。 该值必须在顶点着色器中设置,然后自动发送到GPU管道的下一阶段。 我们不必声明它是“ OUT”变量。
顶点颜色也设置在顶点着色器中。 可变的acolor(我将在所有后续程序中使用)用于存储以下内容:
gl_Position = vec4(position.x, position.y, position.z, 1.0);
aColor = vec4(0.55f, 0.0f, 0.55f, 1.0f);
我们明确指出,我们希望通过使用关键字OUT(OUT VEC4 ACOLOR;)在Vertex Shader中声明该变量时,希望将此值(属性)传递到GPU管道中的下一阶段。
然后,栅格阶段采用三个顶点的位置和颜色(GPU使用索引缓冲区自动处理关联),并创建其定义的三角形的片段。由于顶点着色器中设置的颜色是一个特定的文字值,并且每个顶点都设置为相同的颜色值,因此栅格化过程将插入具有相同颜色值的三个顶点,因此每个片段将是相同的颜色。但是,正如我们将很快看到的那样,有一种方法可以为每个顶点设置不同的颜色值。
在片段着色器中,颜色是输入片段着色器的关键字。重要:变量名称,Acolor与在顶点着色器中声明为“ OUT”变量的变量名称相同。关键字表明片段着色器会产生输出颜色。在此示例中,片段着色器的主体简单地将输出属性设置为输入属性的值。
值得注意的是,在此阶段,在CPU上运行的主要程序将以下数据发送到GPU:
private float[] vertices = {
-0.5f, -0.5f, 0.0f, // Bottom Left
0.5f, -0.5f, 0.0f, // Bottom Right
0.0f, 0.5f, 0.0f // Top middle
};
private int[] indices = {
0, 1, 2
};
仅发送顶点位置数据。 我们还可以将其他数据发送到GPU,例如 每个顶点的颜色。 这些是每个vertex属性,我们稍后会查看。 首先,我们将研究如何使用“制服”
制服可用于将数据从CPU应用程序传递给GPU上的着色器。 可以在着色器程序对象中的顶点或片段着色器访问制服。 通过渲染调用(例如gldrawelements())启动的顶点和片段着色器的每个实例,都可以使用相同的均匀常数。 在随后的渲染呼叫之前,可以更改制服。
但是,重要的是要注意的是,只有一个制服的副本。 例如,并行执行的顶点着色器的每个副本都将共享相同的统一。
private String vertexShaderSource = "#version 330 core\n" +"\n" +"layout (location = 0) in vec3 position;\n" +"\n" +"void main() {\n" +" gl_Position = vec4(position.x, position.y, position.z, 1.0);\n" +"}";private String fragmentShaderSource = "#version 330 core\n" +"\n" +"out vec4 fragColor;\n" +"\n" +"uniform vec4 uniformColor;\n" + // *** colour received from main application"\n" +"void main() {\n" +" fragColor = uniformColor;\n" + "}";
现在,需要更改主应用程序,以将值发送到片段着色器使用的统一。
使用glgetuniformlocation()访问片段着色器中声明的统一,并带有阴影程序和作为参数提供的统一的名称。 然后,方法可以使用gluniform4f来设置统一的新值,通过提供所需的4个浮点值,如该方法的名称所示。 (其他版本可用于设置不同数量的值,例如gluniform3f。)然后像以前的程序一样绘制顶点列表。
public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);double elapsedTime = getSeconds() - startTime;gl.glUseProgram(shaderProgram);float redValue = 0.9f;float greenValue = (float)Math.sin(elapsedTime*5);float blueValue = 0.2f;int vertexColourLocation = gl.glGetUniformLocation(shaderProgram, "uniformColor");gl.glUniform4f(vertexColourLocation, redValue, greenValue, blueValue, 1.0f);gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}
在此示例中,每次使用系统时间使用渲染方法调用渲染方法,而不是每次渲染三角形时发送相同的颜色值。 如下一个代码所示,当调用init()方法时,“类属性启动时间”初始化。 此后,与以前的程序一样,这被用作获得经过时间量的基本价值。
public void init(GLAutoDrawable drawable) { //...rest of method...startTime = getSeconds();}private double startTime;private double getSeconds() {return System.currentTimeMillis()/1000.0;
}
制服是将信息直接传递给着色器的一种方法。 第二种方法是通过缓冲区,就像到目前为止所有程序中的顶点声音一样。 这更灵活,因为可以将更多数据添加到这些缓冲区中。 这称为每个vertex属性。
练习:
1.尝试使用系统时间(即S02_GLEVENTLISTENER.JAVA中的Method Render()中的RedValue,GreenValue和blueValue更改颜色的每个组件。
2.将一个统一的统一着色器添加到vec2值(一个x和y值),然后使用它随时间移动三角形。 (提示:在Render()方法中创建一些变量,例如DX和DY,并使用ElapsedTime变量设置值 - 这类似于RedValue,GreenValue和BlueValue。然后,您需要在顶点着色器中创建的VEC2统一的值 - VEC2为2个浮点值。可以通过将VEC2统一值(V.X和V.Y)添加到顶点位置,例如位置,例如,可以在顶点着色器中完成简单的添加。 x+v.x position.y+v.y。)
解决方案:
public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);double elapsedTime = getSeconds() - startTime;gl.glUseProgram(shaderProgram);float xOffset = (float)Math.sin(elapsedTime)*0.5f;float yOffset = (float)Math.sin(elapsedTime/2)*0.5f;int offsetLocation = gl.glGetUniformLocation(shaderProgram, "uniformOffset");gl.glUniform2f(offsetLocation, xOffset, yOffset);float redValue = 0.9f;float greenValue = (float)Math.sin(elapsedTime*5);float blueValue = 0.2f;int vertexColourLocation = gl.glGetUniformLocation(shaderProgram, "uniformColor");gl.glUniform4f(vertexColourLocation, redValue, greenValue, blueValue, 1.0f);gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}// ***************************************************/* THE SHADER*/private String vertexShaderSource = "#version 330 core\n" +"\n" +"layout (location = 0) in vec3 position;\n" +"\n" +"uniform vec2 uniformOffset;\n" +"\n" +"void main() {\n" +" gl_Position = vec4(position.x+uniformOffset.x, position.y+uniformOffset.y, position.z, 1.0);\n" +"}";
在最后一部分中,我们考虑使用统一来改变碎片颜色。但是,这改变了每个片段的颜色。取而代之的是,我们希望能够给每个顶点一个不同的颜色,然后,栅格化过程将为每个片段产生不同的颜色,因为它双线性将顶点颜色跨三角形插值。我们可以通过向顶点数据添加额外属性来做到这一点。
下一个代码显示了顶点数据结构的更改。每个顶点添加了三个额外的属性。这些是顶点处的颜色的红色,绿色和蓝色值,每个值在0.0…1.0范围内。我还介绍了三个类别描述顶点数据并在处理缓冲区时使用的类属性。 VertexStride变量指出顶点的每组属性有多大,在这种情况下为6 floats,vertexXYZFloats为3,而 vertexColorFloats为3。
private float[] vertices = {-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, // Bottom Left, blue (r=0, g=0, b=1)0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, // Bottom Right, green0.0f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f // Top middle, red};private int vertexStride = 6;private int vertexXYZFloats = 3;private int vertexColourFloats = 3;private int[] indices = {0, 1, 2};
更改顶点着色器以接收属性位置1中顶点的颜色值1。因此,它将希望主应用程序以与我们一直在使用使用使用的位置值相同的方式向每个顶点发送颜色值 相关数据缓冲区。 然后,通过GPU的栅格化过程将每个顶点的颜色发送到片段着色器。 但是,由于每个顶点的颜色不同,因此三角形的片段将是不同的颜色(基于双线性插值位于顶点的值)。
private String vertexShaderSource = "#version 330 core\n" +"\n" +"layout (location = 0) in vec3 position;\n" +"layout (location = 1) in vec3 color;\n" +"out vec3 aColor;\n" +"\n" +"void main() {\n" +" gl_Position = vec4(position, 1.0);\n" +" aColor = color;\n" +"}";private String fragmentShaderSource = "#version 330 core\n" +"in vec3 aColor;\n" +"out vec4 fragColor;\n" +"\n" +"void main() {\n" +" fragColor = vec4(aColor, 1.0f);\n" +"}";
GPU缓冲区必须用顶点属性数据填充。 下一个代码显示了相关方法。 关键部分突出显示。 第一部分与以前的程序相同,并描述了如何将顶点位置数据传输到顶点着色器中的属性位置0。
numxyzfloats为3,numcolorfloats为3。XYZ位置数据以偏移0存储在顶点数据中,即,它是顶点的每组6套浮子中的前3个浮子。 描述颜色数据时,偏移量更改为3*float.bytes,因为此数据是在3个浮子之后出现的,即,这是一个为顶点的每组6个浮子中的第二个3个浮子。 颜色数据被传输到顶点着色器中的属性位置1
private void fillBuffers(GL3 gl) {gl.glGenVertexArrays(1, vertexArrayId, 0);gl.glBindVertexArray(vertexArrayId[0]);gl.glGenBuffers(1, vertexBufferId, 0);gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferId[0]);FloatBuffer fb = Buffers.newDirectFloatBuffer(vertices);gl.glBufferData(GL.GL_ARRAY_BUFFER, Float.BYTES * vertices.length,fb, GL.GL_STATIC_DRAW);int stride = vertexStride; // ***int numXYZFloats = vertexXYZFloats; // ***int offset = 0; // ***gl.glVertexAttribPointer(0, numXYZFloats, GL.GL_FLOAT, false, // ***stride*Float.BYTES, offset); // ***gl.glEnableVertexAttribArray(0); // ***int numColorFloats = vertexColourFloats; // ***offset = numXYZFloats*Float.BYTES; // ***gl.glVertexAttribPointer(1, numColorFloats, GL.GL_FLOAT, false, // ***stride*Float.BYTES, offset); // ***gl.glEnableVertexAttribArray(1); // ***gl.glGenBuffers(1, elementBufferId, 0);IntBuffer ib = Buffers.newDirectIntBuffer(indices);gl.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, elementBufferId[0]);gl.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, Integer.BYTES * indices.length,ib, GL.GL_STATIC_DRAW);gl.glBindVertexArray(0);}
下一个代码给出渲染方法。 这与以前的程序没有变化。 所有工作均已在Fillbuffers方法中完成,以设置如何将数据传输到GPU的描述。 注意如何使用索引。 因此,我们可以轻松地更改数据结构中的顶点和三角形的数量,主要渲染方法将继续起作用。
public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);gl.glUseProgram(shaderProgram);gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}
练习
尝试更改数据结构中的位置和颜色值,然后运行程序以查看效果。 在运行程序之前尝试预测效果。 例如,如果您更改特定顶点的颜色,会发生什么? 如果您更改特定顶点的X,Y,Z位置,会发生什么?
2.动态更改VBO数据
程序正在运行时如何更改顶点的数据: 这可以通过(i)更改CPU上一个或多个顶点的顶点数据,然后将所有顶点数据发送到显示循环的每次迭代的GPU,或(ii)一次发送所有顶点数据在显示循环期间,“触及”到GPU并更改GPU上某些(或全部)顶点的数据。第二个选项意味着我们不必继续发送所有数据(尽管我们仍然必须发送更改的数据)。可以使用类似的方法来更改顶点集合的子集。 (我们将在后面的章节中看到,我们使用矩阵来操纵,即转换3D对象的顶点。)
下一个代码提供了一种方法,可以改变单个顶点的XYZ位置值,以及一种更改单个顶点的颜色值的方法。第一个方法在列表中使用顶点的索引乘以步幅乘以访问顶点数据列表中的特定顶点。第二种方法使用修改版本,该版本考虑了颜色值在单个顶点的一组值集中的XYZ位置值之后出现。因此,vertexXYZFloats = 3(XYZ位置数据中的浮子数量)。相应地更新了渲染方法,以利用该方法来更改值
下一个代码中的两种方法非常相似,并且都依赖于特定的类属性。可能可以改写它们以提高效率,可读性和可维护性。例如,第二种方法的参数可以是r,g和b,而不是x,y和z来提高可读性/可理解性。但是,我已经以这种方式编写了它们,以使两种方法的相似之处清楚地关注访问GPU上的数据时发生的事情。
注意:在FillBuffers()的先前版本中,Glbufferdata()在设置顶点数据的缓冲区时一直在使用GL.GL_STATIC_DRAW。现在,随着每个调用渲染的调用,数据都在更改,这将更改为gl.gl_dynamic_draw。 (关于是否有必要将gl.gl_static_draw更改为gl.gl_dynamic_draw有一些论点,因为现代硬件/驱动程序可以自动管理此问题。)
private void replaceVBO_XYZ(GL3 gl, int index, float x, float y, float z) {float[] aVertex = {x,y,z};gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferId[0]);FloatBuffer fb = Buffers.newDirectFloatBuffer(aVertex);gl.glBufferSubData(GL.GL_ARRAY_BUFFER, Float.BYTES * index * vertexStride, Float.BYTES * aVertex.length, fb);gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);}private void replaceVBO_RGB(GL3 gl, int index, float x, float y, float z) {float[] aVertex = {x,y,z};gl.glBindBuffer(GL.GL_ARRAY_BUFFER, vertexBufferId[0]);FloatBuffer fb = Buffers.newDirectFloatBuffer(aVertex);gl.glBufferSubData(GL.GL_ARRAY_BUFFER, Float.BYTES * (index * vertexStride + vertexXYZFloats), // ***Float.BYTES * aVertex.length, fb);gl.glBindBuffer(GL.GL_ARRAY_BUFFER, 0);}
下一代码利用上一个方法在显示循环期间更改三角形的所有三个顶点。 每个顶点的XYZ位置通过使用ElapsedTime变量而改变。 SIN和COS的变化用于不同的改变每个顶点。
注意:在上一个代码中没有检查错误,因此希望在程序列表中的调用方法下一代码提供有效的顶点索引!
public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);double elapsedTime = getSeconds() - startTime;replaceVBO_XYZ(gl, 0, (float)Math.sin(elapsedTime), (float)Math.cos(elapsedTime), 0);replaceVBO_XYZ(gl, 1, (float)Math.sin(elapsedTime)*0.5f,(float)Math.cos(elapsedTime)*0.5f, 0);replaceVBO_XYZ(gl, 2, (float)Math.cos(elapsedTime*0.5),(float)Math.sin(elapsedTime*0.5), 0);gl.glUseProgram(shaderProgram);gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}
练习:
1.除了XYZ值之外,还要尝试更改顶点的RGB颜色值。
解决方案:
private float inRange(double x) {x = (x+1)*0.5;if (x<0) return 0f;else if (x>1) return 1.0f;else return (float)x;}public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);double elapsedTime = getSeconds() - startTime;replaceVBO_XYZ(gl, 0, (float)Math.sin(elapsedTime), (float)Math.cos(elapsedTime),0);replaceVBO_XYZ(gl, 1, (float)Math.sin(elapsedTime)*0.5f, (float)Math.cos(elapsedTime)*0.5f,0);replaceVBO_XYZ(gl, 2, (float)Math.cos(elapsedTime*0.5), (float)Math.sin(elapsedTime*0.5),0);replaceVBO_RGB(gl, 0, inRange(Math.sin(elapsedTime)), inRange(Math.cos(elapsedTime)), inRange(Math.sin(elapsedTime)));replaceVBO_RGB(gl, 1, inRange(Math.cos(elapsedTime)), inRange(Math.sin(elapsedTime)), inRange(Math.sin(elapsedTime)));replaceVBO_RGB(gl, 2, inRange(Math.sin(elapsedTime)), inRange(Math.cos(elapsedTime)), inRange(Math.cos(elapsedTime)));gl.glUseProgram(shaderProgram);gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}
3.着色器类
在所有先前的程序中,设置着色器的代码也保持不变,即使顶点和片段着色器的来源已更改。将着色器代码分为单独的类并从文件中加载着色器的阴暗的代码是有意义的。新类是Shader.java。然后,新代码在s05_gleventlistener.java中使用和文本文件vs_s05.txt和fs_s05.txt用于顶点和片段shader源代码。
程序列表中的大多数代码与以前的示例相同,即method方法compileandlink()。构造函数从相关的文本文件加载源代码。 s05_gleventlistener.java使用着着色器类。称为“类型着色器”的私有变量被声明为类的属性,并且在方法inationalise()中创建了一个实例。然后,方法Render()可以将其与呼叫着色器(GL)一起使用。 Shader.java中还有一些额外的方法,可用于以与Joey的班级相同的方式在着色器中设置特定的统一值。
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.charset.Charset;
import com.jogamp.opengl.*;
import com.jogamp.opengl.util.glsl.*; public class Shader {private static final boolean DISPLAY_SHADERS = false;private int ID;private String vertexShaderSource;private String fragmentShaderSource;/* The constructor */public Shader(GL3 gl, String vertexPath, String fragmentPath) {try {vertexShaderSource = new String(Files.readAllBytes(Paths.get(vertexPath)), Charset.defaultCharset());fragmentShaderSource = new String(Files.readAllBytes(Paths.get(fragmentPath)), Charset.defaultCharset());}catch (IOException e) {e.printStackTrace();}if (DISPLAY_SHADERS) display();ID = compileAndLink(gl);}public int getID() {return ID;}public void use(GL3 gl) {gl.glUseProgram(ID);}public void setInt(GL3 gl, String name, int value) {int location = gl.glGetUniformLocation(ID, name);gl.glUniform1i(location, value);}public void setFloat(GL3 gl, String name, float value) {int location = gl.glGetUniformLocation(ID, name);gl.glUniform1f(location, value);}public void setFloat(GL3 gl, String name, float f1, float f2) {int location = gl.glGetUniformLocation(ID, name);gl.glUniform2f(location, f1, f2);}public void setFloat(GL3 gl, String name, float f1, float f2, float f3) {int location = gl.glGetUniformLocation(ID, name);gl.glUniform3f(location, f1, f2, f3);}public void setFloat(GL3 gl, String name, float f1, float f2, float f3, float f4) {int location = gl.glGetUniformLocation(ID, name);gl.glUniform4f(location, f1, f2, f3, f4);}private void display() {System.out.println("***Vertex shader***");System.out.println(vertexShaderSource);System.out.println("\n***Fragment shader***");System.out.println(fragmentShaderSource);}private int compileAndLink(GL3 gl) {String[][] sources = new String[1][1];sources[0] = new String[]{ vertexShaderSource };ShaderCode vertexShaderCode = new ShaderCode(GL3.GL_VERTEX_SHADER, sources.length, sources);boolean compiled = vertexShaderCode.compile(gl, System.err);if (!compiled)System.err.println("[error] Unable to compile vertex shader: " + sources);sources[0] = new String[]{ fragmentShaderSource };ShaderCode fragmentShaderCode = new ShaderCode(GL3.GL_FRAGMENT_SHADER, sources.length, sources);compiled = fragmentShaderCode.compile(gl, System.err);if (!compiled)System.err.println("[error] Unable to compile fragment shader: " + sources);ShaderProgram program = new ShaderProgram();program.init(gl);program.add(vertexShaderCode);program.add(fragmentShaderCode);program.link(gl, System.out);if (!program.validateProgram(gl, System.out))System.err.println("[error] Unable to link program");return program.program();}}
练习:
1.调整顶点着色器(VS_S05.TXT ),以便将三角形颠倒绘制。 (提示:这可以通过在将顶点着色器中的位置值分配给gl_position时通过操纵位置值来完成。)
解决方案:
解决方案是:
(i)可以操纵为顶点着色器提供的坐标;
(ii)可以在任何文本编辑器中更改顶点着色器,
而且该程序不必重新编译可以直接使用它。
#version 330 corelayout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
out vec3 aColor;void main() {gl_Position = vec4(position.x, -position.y, position.z, 1.0);aColor = color;
}
2.通过统一指定水平偏移,然后使用此偏移值将三角形移动到顶点着色器中的屏幕右侧。
解决方案:
public void render(GL3 gl) {gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);shader.use(gl);float xOffset = 0.4f;float yOffset = 0.0f;shader.setFloat(gl, "uniformOffset", xOffset, yOffset); // call to method in Shader class// could instead only pass a single float rather than two floats// uniform variable in the shader would then be a float, not a vec2gl.glBindVertexArray(vertexArrayId[0]);gl.glDrawElements(GL.GL_TRIANGLES, indices.length, GL.GL_UNSIGNED_INT, 0);gl.glBindVertexArray(0);}The vertex shader:#version 330 corelayout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
out vec3 aColor;
uniform vec2 uniformOffset;void main() {gl_Position = vec4(position.x+uniformOffset.x, position.y, position.z, 1.0);aColor = color;
}