OpenGL ES 学习教程(二) 可编程管线,Shader,一个彩色三角形!

如果你还手里有老老版  OpenGL编程指南 ,打开来看一看,在真正开始用 OpenGL绘制图形时,使用的是下面的API:

glBegin(GL_TRIANGLES);							// 绘制三角形

		glVertex3f( 0.0f, 1.0f, 0.0f);					// 上顶点

		glVertex3f(-1.0f,-1.0f, 0.0f);					// 左下

		glVertex3f( 1.0f,-1.0f, 0.0f);					// 右下

glEnd();	

这就是已经被抛弃的 固定管线编程 。

http://blog.csdn.net/huutu

先去洗个脑,扔掉你所学过的那些 固定管线 编程 的OpenGL API,留下你脑袋里面的强大的图形数学知识,让我们进入可编程管线。


上图中虚线是 固定管线,黑色粗实线 是 可编程管线 。

可以看到这里 用了 顶点程序片元程序字样,就是说,这两个过程是可编程的,要我们自己写代码去控制。

顶点程序就是常说的 顶点 Shader ,后缀一般是vsh  , 片元程序 就是常说的 片段Shader,后缀一般是 fsh 。

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

好。

我们编写的 Shader 代码是可以运行在GPU上的,既然是作为一段程序,那么和我们平时写的C++代码一样,想要最后运行起来也要经过一个流程。

1、创建一个项目 glCreateProgram

2、创建一个代码文件 glCreateShader

3、输入代码 glShaderSource

4、编译代码 glCompileShader

5、查看编译是否错误 glGetShaderiv

6、把Shader添加到项目中 glAttachShader

7、链接每个Shader glLinkProgram

8、获取链接状态 glGetProgramiv

9、运行这个项目 glUseProgram


下面是一个Shader 的完整例子,包含上面的流程

#pragma once
#include
   
   
    
    
#include
    
    
     
     
#include
     
     
      
      

class ShaderID
{
public:
	ShaderID()
	{
		m_shaderId = -1;
	}

	int m_shaderId;
};

//封装OpenGL 的glProgram的一些信息;
class GLProgram
{
public:
	int m_programId;
	ShaderID m_vertexShader;
	ShaderID m_fragmentShader;


public:
	GLProgram()
	{
		m_programId = -1;
	}

public:
	//加载shader并且创建glProgram;
	bool createProgram(const char *vertex, const char *fragment)
	{
		bool error = false;
		do
		{
			if (vertex)
			{
				//创建Shader;
				m_vertexShader.m_shaderId = glCreateShader(GL_VERTEX_SHADER);
				//源码;
				glShaderSource(m_vertexShader.m_shaderId, 1, &vertex, 0);
				//编译;
				glCompileShader(m_vertexShader.m_shaderId);

				GLint compileStatus;
				glGetShaderiv(m_vertexShader.m_shaderId, GL_COMPILE_STATUS, &compileStatus);

				error = (compileStatus == GL_FALSE);
				if (error)
				{
					GLchar message[256];
					glGetShaderInfoLog(m_vertexShader.m_shaderId, sizeof(message), 0, message);
					assert((message && 0) != 0);
					break;
				}
			}

			if (fragment)
			{
				//创建Shader;
				m_fragmentShader.m_shaderId = glCreateShader(GL_FRAGMENT_SHADER);
				//源码;
				glShaderSource(m_fragmentShader.m_shaderId, 1, &fragment, 0);
				//编译;
				glCompileShader(m_fragmentShader.m_shaderId);

				GLint compileStatus;
				glGetShaderiv(m_fragmentShader.m_shaderId, GL_COMPILE_STATUS, &compileStatus);

				error = (compileStatus == GL_FALSE);
				if (error)
				{
					GLchar message[256];
					glGetShaderInfoLog(m_fragmentShader.m_shaderId, sizeof(message), 0, message);
					assert((message && 0) != 0);
					break;
				}
			}

			//创建gl程序;
			m_programId = glCreateProgram();

			//代码添加到program中;
			if (m_vertexShader.m_shaderId)
			{
				glAttachShader(m_programId, m_vertexShader.m_shaderId);
			}
			if (m_fragmentShader.m_shaderId)
			{
				glAttachShader(m_programId, m_fragmentShader.m_shaderId);
			}

			//链接;
			glLinkProgram(m_programId);

			GLint linkStatus;
			glGetProgramiv(m_programId, GL_LINK_STATUS, &linkStatus);

			if (linkStatus == GL_FALSE)
			{
				GLchar message[256];
				glGetProgramInfoLog(m_programId, sizeof(message), 0, message);
				assert((message && 0) != 0);
				break;
			}

			//使用program;
			//glUseProgram(m_programId);

		} while (false);
		
		if (error)
		{
			if (m_fragmentShader.m_shaderId)
			{
				glDeleteShader(m_fragmentShader.m_shaderId);
				m_fragmentShader.m_shaderId = 0;
			}

			if (m_vertexShader.m_shaderId)
			{
				glDeleteShader(m_vertexShader.m_shaderId);
				m_vertexShader.m_shaderId = 0;
			}

			if (m_programId)
			{
				glDeleteProgram(m_programId);
				m_programId = 0;
			}
		}

		return true;
	}

	virtual void begin()
	{
		glUseProgram(m_programId);
	}

	virtual void end()
	{
		glUseProgram(0);
	}
};
     
     
    
    
   
   
转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

一对简单的 Vertex Shader 和 Fragment Shader 可能长的像下面

const char* vertexShader=
		{
			"precision lowp float;"
			"uniform mat4 m_mvp;"
			"attribute vec3 m_position;"
			"attribute vec4 m_color;"

			"varying vec4 m_outColor;"

			"void main()"
			"{"
			"	vec4 pos=vec4(m_position,1);"
			"	gl_Position=m_mvp*pos;"
			"	m_outColor=m_color;"
			"}"
		};

		const char* fragmentShader =
		{
			"precision lowp float;"
			"varying vec4 m_outColor;"

			"void main()"
			"{"
			"	gl_FragColor=m_outColor;"
			"}"
		};

uniform 、attribute 都是变量修饰符。这两个修饰符修饰的变量 ,我们可以在 C++ 代码中获取到然后给他们赋值。

varying 修饰符修饰的变量代表在 Vertex 和 Fragment 之间传递。不能从C++代码中操作。


uniform mat4 m_mvp  

我们从C++中传入 MVP 矩阵

attribute vec3 m_position

我们从C++中传入 顶点位置

attribute vec4 m_color

我们从C++中传入 顶点颜色


好了。下面就让我们来设置这些变量。

这里继承了上面的类

#pragma once
#include"GLProgram.h"

class GLProgram_Color :public GLProgram
{
public:

	//传入shader中的值;
	GLint m_position;
	GLint m_color;
	GLint m_mvp;

public:
	GLProgram_Color()
	{
		m_position = -1;
		m_color = -1;
		m_mvp = -1;
	}

	~GLProgram_Color()
	{}

	GLint getPositionAttribute() const
	{
		return m_position;
	}

	GLint getColorAttribute() const
	{
		return m_color;
	}

	GLint getMVP() const
	{
		return m_mvp;
	}

	//attribute:只能在Vertexshader中使用;
	//Unifrom:在Vertex和Fragment中共享使用,且不能被修改;
	//Varying:从Vertex传递数据到Fragment中使用;
	virtual bool Initialize()
	{
		const char* vertexShader=
		{
			"precision lowp float;"
			"uniform mat4 m_mvp;"
			"attribute vec3 m_position;"
			"attribute vec4 m_color;"

			"varying vec4 m_outColor;"

			"void main()"
			"{"
			"	vec4 pos=vec4(m_position,1);"
			"	gl_Position=m_mvp*pos;"
			"	m_outColor=m_color;"
			"}"
		};

		const char* fragmentShader =
		{
			"precision lowp float;"
			"varying vec4 m_outColor;"

			"void main()"
			"{"
			"	gl_FragColor=m_outColor;"
			"}"
		};

		bool ret = createProgram(vertexShader, fragmentShader);
		if (ret)
		{
			m_position = glGetAttribLocation(m_programId, "m_position");
			m_color = glGetAttribLocation(m_programId, "m_color");
			m_mvp = glGetUniformLocation(m_programId, "m_mvp");
		}
		return ret;
	}

	virtual void begin()
	{
		glUseProgram(m_programId);
		glEnableVertexAttribArray(m_position);
		glEnableVertexAttribArray(m_color);
	}

	virtual void end()
	{
		glDisableVertexAttribArray(m_position);
		glDisableVertexAttribArray(m_color);
		glUseProgram(0);
	}
};

转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn

可以看到,在创建Shader 并且编译成功之后,就可以获取 Shader 中的变量了。

bool ret = createProgram(vertexShader, fragmentShader);
if (ret)
{
    m_position = glGetAttribLocation(m_programId, "m_position");
    m_color = glGetAttribLocation(m_programId, "m_color");
    m_mvp = glGetUniformLocation(m_programId, "m_mvp");
}

然后像下面这样去传入值 到这几个变量

glUniformMatrix4fv(m_program.m_mvp, 1, false, &proj[0][0]);

glVertexAttribPointer(m_program.m_position, 2, GL_FLOAT, false, sizeof(glm::vec3), pos);
glVertexAttribPointer(m_program.m_color, 4, GL_FLOAT, false, sizeof(glm::vec4), color);

 

这里说下MVP

MVP是一个矩阵,是由 模型矩阵 乘以 视图矩阵 乘以 投影矩阵 得来的,代表对世界坐标系中的一个点执行的一系列 操作。

想详细了解的请百度相关资料,关键字:OpenGL 矩阵变换

好了,我们在教程一的基础上添加了 一些修改,创建了Shader、给Shader设置了值,并且引入了一个三方库 GLM 来进行矩阵的计算。

示例程序下载:

http://pan.baidu.com/s/1eQjW5O2


转自http://blog.csdn.net/huutu http://www.thisisgame.com.cn



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
OpenGL可编程管线OpenGL Programmable Pipeline)是一种用于图形渲染的编程框架,通过编写顶点/片元着色器程序来实现对图形渲染过程的控制。而拾取(Picking)是指通过特定的手段选择或者捕捉到场景中的一个或多个物体。 在OpenGL可编程管线中,实现拾取可以通过以下步骤完成: 1. 为需要拾取的物体设置唯一的标识符(ID):在渲染场景时,为每个物体分配一个唯一的ID,可以通过OpenGL提供的函数生成一个一致的ID。 2. 渲染场景:正常渲染整个场景,使用顶点和片元着色器进行图形渲染,但是需要使用ID来标识每个物体。 3. 读取选择像素:通过获取鼠标位置对应的像素颜色,获取用户点击(或者鼠标悬停)的位置的像素颜色。 4. 解析像素颜色:将获取到的像素颜色转换为对应的ID。 5. 处理拾取结果:根据解析得到的ID,找到对应的物体,对其进行进一步的处理。 需要注意的是,拾取过程中必须保证物体的ID是唯一的,并且ID应该与物体渲染时使用的颜色没有冲突。此外,为了提高效率,可以利用一些空间划分的方法(如包围盒)来快速排除不需要的物体。 总之,OpenGL可编程管线提供了一种灵活的方式来实现图形渲染的控制,而拾取则是通过对特定物体设置唯一的标识符,并通过像素颜色解析来选择或捕获到场景中的物体。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值