上一个教程向我们展示了如何在屏幕上画一个三角形。但是,我说过,那是一种古老的方式,即使它能够正常运行,但是现在这已经不是“正确”的方式。上篇文章中我们将几何发送到GPU的方式是所谓的“即时模式”,它非常简单,但是已经不再推荐使用。

在本教程中,我们将要实现同样的最终目标,但是我们将以更复杂的方式来做事情,疯了么大哥?

我们选择更麻烦的编写方式,是为了更有效率,更快速和可扩展性。

我们将像以前的教程一样开始,我将引用原文几次,所以如果还没有看过上一篇的话,请抽空看看。

Part 1:设置

要开始,我们需要创建一个新的项目,引用OpenTK和System.Drawing,同上一个教程。将其命名为OpenTKTutorial2。

Part 2:编码

首先,我们需要再次做一些基础工作,就像第一个教程那样。添加一个名为“Game”的新类。使它成为GameWindow的子类(您需要为OpenTK添加一个using指令才能使用该类)。

差不多是这样:

using OpenTK;

namespace OpentkTutorials2
{
class Game : GameWindow
{
}
}

回到Program.cs,添加代码:

namespace OpentkTutorials2
{
class Program
{
static void Main(string[] args)
{
using (var game = new Game())
{
game.Run(30.0);
}
}
}
}

Onload方法和OnRenderFrame方法参照上一个教程做就行了。

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//修改窗口标题
Title = "Hello OpenTK!";
//设置背景颜色为,额,不知道什么蓝(需要添加 OpenTK.Graphics.OpenGL and System.Drawing引用)
GL.ClearColor(Color.CornflowerBlue);
}
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); SwapBuffers();
}

好了,从这里开始,我们可以学点新的东西了!

我们首先需要做的是创建我们的着色器(Shader)。现代OpenGL使用它获知如何绘制给出的值。我们将使用两种着色器:顶点着色器(Vertex Shader)和片段着色器(Fragment Shader)。 顶点着色器告诉显卡正在绘制的形状中的点的信息。片段着色器决定绘制到屏幕时形状的每个像素的颜色。我们将要使用的代码非常简单,但是我们可以使用类似于即时模式的风格操作。OpenGL的着色器以类C语言的脚本语言编写,称为GLSL(DirectX使用稍微不同的语言,称为HLSL)。

译者注:有另外一篇非常好的文章讲GLSL,推荐先阅读以更深入了解GLSL:LearnOpenGL CN,该系列教程也非常推荐阅读。

将一个文本文件添加到您的项目中,名为“vs.glsl”。 这将存储我们的顶点着色器:

#version 330

in vec3 vPosition;
in vec3 vColor;
out vec4 color;
uniform mat4 modelview; void
main()
{
gl_Position = modelview * vec4(vPosition, 1.0); color = vec4( vColor, 1.0);
}

注意:对于着色器文件,您可能需要告诉IDE将其复制到输出目录(设置文件为始终复制),否则程序将无法找到它们!

第一行告诉链接器正在使用哪个版本的GLSL。

“in”行表示每个顶点具有的不同变量。“out”变量被发送到图形流水线的下一部分,在其中进行插值,以便跨片段平滑过渡。我们通常发送每个顶点的颜色。 “vec3”类型是指具有三个值的向量,“vec4”是具有四个值的向量。

这里还有一个“uniform”变量,对于整个被绘制的对象来说,该变量是相同的。 这将有我们的转换矩阵,所以我们可以一次性改变对象中的顶点。我们还没有用到它,但我们很快就会使用它的。

我们的片段着色器更简单。 将以下内容另存为“fs.glsl”:

#version 330

in vec4 color;
out vec4 outputColor; void
main()
{
outputColor = color;
}

它只是获得上一个着色器输出的颜色变量(注意它现在是“输入”的“in”),并将输出设置为该颜色。

现在我们有了这些着色器,接下来我们需要指示显卡去使用它们。首先,我们需要告诉OpenTK创建一个新的程序对象(program)。 它将以可用的形式存储的这些着色器。

首先,定义程序的ID(它的地址)变量,置于其他函数之外。我们在代码中不存储程序对象本身,而是存储一个可以引用的地址,程序其本身将存储在显卡中

int pgmID;

在Game类中创建一个新的函数,称为initProgram。在这个函数中,我们将首先调用GL.CreateProgram()函数,该函数返回一个新程序对象的ID,我们将它存储在pgmID中。

void initProgram()
{
pgmID = GL.CreateProgram();
}

然后我们需要写一个加载器来读取我们的着色器代码并添加它们。此函数需要获取文件名和一些信息,并返回创建的着色器的地址。

它应该看起来像这样:

void loadShader(String filename,ShaderType type, int program, out int address)
{
address = GL.CreateShader(type);
using (StreamReader sr = new StreamReader(filename))
{
GL.ShaderSource(address, sr.ReadToEnd());
}
GL.CompileShader(address);
GL.AttachShader(program, address);
Console.WriteLine(GL.GetShaderInfoLog(address));
}

上面代码将创建一个新的着色器(使用ShaderType枚举中的值),为其加载代码,编译,并将其添加到我们的程序中。它还会在控制台中将发现的任何错误打印出来,当在着色器中发生错误时,这是非常好的(如果您使用过时的代码,它也会警告)。

现在我们有了这个,我们来添加我们的着色器。首先我们在类上定义两个变量:

      int vsID;
int fsID;

这些将存储我们两个着色器的地址。 现在,我们要使用我们从文件中加载着色器的功能。

将以下代码添加到initProgram中:

	loadShader("vs.glsl", ShaderType.VertexShader, pgmID, out vsID);
loadShader("fs.glsl", ShaderType.FragmentShader, pgmID, out fsID);

现在,添加了着色器,程序需要链接。像C代码一样,代码首先被编译,然后被链接,完成从人类可读的代码到需要的机器语言的转变。

然后再添加:

GL.LinkProgram(pgmID);
Console.WriteLine(GL.GetProgramInfoLog(pgmID));

这将链接它,并告诉我们是否有错误。

着色器现在被添加到我们的程序中,但是我们需要告诉程序更多的信息才能正常工作。我们在我们的顶点着色器上有多个输入,所以我们需要告诉它们地址来给出顶点的着色器位置和颜色信息。

将此代码添加到Game类中:

	  int attribute_vcol;
int attribute_vpos;
int uniform_mview;

我们在这里定义三个变量,存储每个变量的位置,以供将来引用。往后我们将需要使用这些值,所以我们应该保持简单。要获取每个变量的地址,我们使用GL.GetAttribLocationGL.GetUniformLocation函数。每个都使用着色器中的程序的ID和变量的名称。

在initProgram结尾处添加:

attribute_vpos = GL.GetAttribLocation(pgmID, "vPosition");
attribute_vcol = GL.GetAttribLocation(pgmID, "vColor");
uniform_mview = GL.GetUniformLocation(pgmID, "modelview"); if (attribute_vpos == -1 || attribute_vcol == -1 || uniform_mview == -1)
{
Console.WriteLine("Error binding attributes");
}

上面代码将存储我们需要的值,并且还要做一个简单的检查,以确保找到属性。

译者注:也可以不在C#代码中指定,而在shader代码中使用layout (location = x)的方式指定。具体用法可以参见上文中说的

现在我们的着色器和程序已经建立起来了,但是我们还需要给他们一些东西绘制。为此,我们将使用顶点缓冲区对象(VBO)。 当您使用VBO时,首先需要让显卡创建一个,然后绑定到它并发送你的信息。最后,当DrawArrays函数被调用时,缓冲区中的信息将被一次性发送到着色器并绘制到屏幕上。

像着色器的变量一样,我们也需要存储地址以供将来使用:

int vbo_position;
int vbo_color;
int vbo_mview;

创建缓冲区非常简单。在initProgram中添加:

GL.GenBuffers(1, out vbo_position);
GL.GenBuffers(1, out vbo_color);
GL.GenBuffers(1, out vbo_mview);

这将生成3个单独的缓冲区并将其地址存储在我们的变量中。对于像这样的多个缓冲区,有一个可以生成多个缓冲区并将它们存储在数组中的选项,但是为了简单起见,在这里我们将它们保留在单独的int中。

这些缓冲区将需要一些数据。位置和颜色都为Vector3类型,模型视图为Matrix4类型。我们需要将它们存储在一个数组中,这样可以更有效地将数据发送到缓冲区。

向Game类添加三个变量:

Vector3[] vertdata;
Vector3[] coldata;
Matrix4[] mviewdata;

这个例子中,我们将在onLoad中设置这些值,并调用initProgram():

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e); initProgram(); vertdata = new Vector3[] { new Vector3(-0.8f, -0.8f, 0f),
new Vector3( 0.8f, -0.8f, 0f),
new Vector3( 0f, 0.8f, 0f)}; coldata = new Vector3[] { new Vector3(1f, 0f, 0f),
new Vector3( 0f, 0f, 1f),
new Vector3( 0f, 1f, 0f)}; mviewdata = new Matrix4[]{
Matrix4.Identity
}; Title = "Hello OpenTK!";
GL.ClearColor(Color.CornflowerBlue);
GL.PointSize(5f);
}

数据存储完毕,我们就可以发送到缓冲区了。我们需要为OnUpdateFrame函数添加另一个重载。首先是绑定到缓冲区:

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position);

这就告诉OpenTK,如果我们发送任何数据,我们将使用该缓冲区。接下来,我们会发送数据:

            GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(vertdata.Length * Vector3.SizeInBytes), vertdata, BufferUsageHint.StaticDraw);

这段代码告诉我们,我们发送的长度为(vertdata.Length * Vector3.SizeInBytes)的vertdata到缓冲区。最后,我们需要告诉它使用这个缓冲区(最后一个绑定到)vPosition变量,这将需要3个float值:

           GL.VertexAttribPointer(attribute_vpos, 3, VertexAttribPointerType.Float, false, 0, 0);

所以,最后合起来:

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_position);
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(vertdata.Length * Vector3.SizeInBytes), vertdata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(attribute_vpos, 3, VertexAttribPointerType.Float, false, 0, 0); GL.BindBuffer(BufferTarget.ArrayBuffer, vbo_color);
GL.BufferData<Vector3>(BufferTarget.ArrayBuffer, (IntPtr)(coldata.Length * Vector3.SizeInBytes), coldata, BufferUsageHint.StaticDraw);
GL.VertexAttribPointer(attribute_vcol, 3, VertexAttribPointerType.Float, true, 0, 0);

我们还需要发送模型视图矩阵(Model-View Matrix):

             GL.UniformMatrix4(uniform_mview, false, ref mviewdata[0]);

最后,我们要清除缓冲区绑定,并将其设置为与我们的着色器一起使用该程序:

            GL.UseProgram(pgmID);
GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

快要大功告成了! 现在我们将数据、着色器发送到显卡,但是我们还需要绘制他们。在我们的OnRenderFrame函数中,首先我们需要告诉它使用我们想要的变量:

            GL.EnableVertexAttribArray(attribute_vpos);
GL.EnableVertexAttribArray(attribute_vcol);

然后我们告诉它如何绘制它们:

GL.DrawArrays(PrimitiveType.Triangles, 0, 3);

最后是清理工作:

GL.DisableVertexAttribArray(attribute_vpos);
GL.DisableVertexAttribArray(attribute_vcol); GL.Flush();

最终看起来是这样子:

        protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Viewport(0, 0, Width, Height);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.Enable(EnableCap.DepthTest);< GL.EnableVertexAttribArray(attribute_vpos);
GL.EnableVertexAttribArray(attribute_vcol); GL.DrawArrays(BeginMode.Triangles, 0, 3); GL.DisableVertexAttribArray(attribute_vpos);
GL.DisableVertexAttribArray(attribute_vcol); GL.Flush();
SwapBuffers();
}

如果你运行这些代码,效果是不是很熟悉?


本系列教程翻译自Neo Kabuto's Blog。已经取得作者授权。

本文原文地址http://neokabuto.blogspot.com/2013/03/opentk-tutorial-2-drawing-triangle.html

原文代码可以在github上找到。

OpenTK教程-2绘制一个三角形(正确的方法)的更多相关文章

  1. OpenTK教程-2绘制一个三角形(正确的方式)

    上一个教程向我们展示了如何在屏幕上画一个三角形.但是,我说过,那是一种古老的方式,即使它能够正常运行,但是现在这已经不是"正确"的方式.上篇文章中我们将几何发送到GPU的方式是所谓 ...

  2. OpenTK教程-1绘制一个三角形

    OpenTK的官方文档是真心的少,他们把怎么去安装OpenTK说的很清楚,但是也就仅限于此,这有一篇learn opentk in 15的教程(链接已经失效,译者注),但是并不完美.你可以在15分钟内 ...

  3. 【OpenGL4.0】GLSL渲染语言入门与VBO、VAO使用:绘制一个三角形 【转】

    http://blog.csdn.net/xiajun07061225/article/details/7628146 以前都是用Cg的,现在改用GLSL,又要重新学,不过两种语言很多都是相通的. 下 ...

  4. WebGL简易教程(三):绘制一个三角形(缓冲区对象)

    目录 1. 概述 2. 示例:绘制三角形 1) HelloTriangle.html 2) HelloTriangle.js 3) 缓冲区对象 (1) 创建缓冲区对象(gl.createBuffer( ...

  5. Unity3D学习笔记1——绘制一个三角形

    目录 1. 绪论 2. 概述 3. 详论 3.1. 准备 3.2. 实现 3.3. 解析 3.3.1. 场景树对象 3.3.2. 绘制方法 4. 结果 1. 绪论 最近想学习一下Unity3d,无奈发 ...

  6. [Modern OpenGL系列(三)]用OpenGL绘制一个三角形

    本文已同步发表在CSDN:http://blog.csdn.net/wenxin2011/article/details/51347008 在上一篇文章中已经介绍了OpenGL窗口的创建.本文接着说如 ...

  7. Opentk教程系列-1绘制一个三角形

    本系列教程翻译自Neo Kabuto's Blog.已经取得作者授权. 本文原文地址http://neokabuto.blogspot.com/2013/02/opentk-tutorial-1-op ...

  8. Android快乐贪吃蛇游戏实战项目开发教程-03虚拟方向键(二)绘制一个三角形

    该系列教程概述与目录:http://www.cnblogs.com/chengyujia/p/5787111.html 一.绘制三角形 在上一篇文章中,我们已经新建了虚拟方向键的自定义控件Direct ...

  9. Stage3D学习笔记(二):使用GPU绘制一个三角形

    我们需要使用到Adobe自家提供的AGALMiniAssembler代码类,可以在网下进行下载: 关于AGAL的入门知识可以参考下面的文章: AGAL介绍系列文章(第一部分)AGAL介绍系列文章(第二 ...

随机推荐

  1. Chrome浏览器扩展开发系列之七:override页面

    Chrome浏览器通常提供了一些默认页面,如标签管理器页面chrome://bookmarks.浏览历史记录页面chrome://history或新建Tab页面chrome://newtab等. Ch ...

  2. 一颗简单的hibernate栗子

    Hibernate是一个开源的ORM框架,顾名思义,它的核心思想即ORM(Object Relational Mapping,对象关系映射),可以通过对象来操作数据库中的信息,据说开发者一开始是不太熟 ...

  3. bootstrap栅栏系统css中的col-xs-*、col-sm-*、col-md-* 的意义以及 bootstrap一个标签中,同时有 col-xs , col-sm , col-md , col-lg的理解

    摘要: bootstrap栅栏系统css中的col-xs-*.col-sm-*.col-md-* 的意义: .col-xs- 超小屏幕 手机 (<768px) .col-sm- 小屏幕 平板 ( ...

  4. 华为OJ之自动售货系统

    本题主要难点有两部分: 1,找零算法.如何找零应该是最具技巧性的部分,根据已有的硬币金额分布,对应的解决办法可能会有不同.本题中的1,2,5,10这种情况满足贪心性质,故我们简单的用贪心算法的思想来解 ...

  5. (转)IntelliJ IDEA 破解方法

    1.下载破解包http://pan.baidu.com/s/1gf9fXx5 2.解压并打开选中的文件 3.如图 4.IDEA选择License Server输入 http://127.0.0.1:1 ...

  6. 自动生成proto Js语句

    在与后端的WebSocket通信时,前端要带一个proto文件是一个累赘的事情.首先是明显的曝光了协议实体对象,再一个浏览器客户端很容易会缓存该文件,新的协议更新可能导致客户端不能使用,另外在cdn服 ...

  7. maven相关的学习资料

    1, maven的settings配置文件详解: http://blog.csdn.net/jinshuaiwang/article/details/23686099 2,maven原理---翡青的博 ...

  8. STM32初学Keil4编译时出现 Error:Failed to execute 'BIN40/Armcc'

    一种是在系统开始--运行里输入cmd,查看armcc状态.详情见推文: http://blog.csdn.net/hicui/article/details/7350805(笔记记录,请勿见怪) 都没 ...

  9. iOS源码博文集锦1

    iOS精选源码 iOS一种弹出视图效果带动画 导航栏显示渐变色,类似qq一样 一分钟找到重力方向 简单高度自定义的日历.可根据项目的需求灵活修改布局 类似于UITableView且极简的图片浏览器 小 ...

  10. 写个 Hello world - 前端从入坑到弃坑系列教程(1)

    这是一个系列教程<前端从入坑到弃坑>的第一篇. HTML 是什么 说白了,HTML 就是网页的内容.比如你现在正在阅读的这个网页的内容,就是 HTML.如果你还不明白,请继续往下阅读. 写 ...