跳至內容

OpenGL編程/現代OpenGL介紹

維基教科書,自由的教學讀本
我們的首個程序

簡介

[編輯]

大多數OpenGL方面的文檔都在使用一些已被廢棄或不贊成使用的特性,尤其是「固定流水線」。 OpenGL 2.0及其後包含了一個可編程流水線(programmable pipeline)——可編程部分在着色器(shader)中完成,由GLSL這麼一種像C的語言寫就。

本文檔寫作的目標羣體是那些正在學習OpenGL並且希望一開始就使用現代OpenGL的人。 可編程流水線更加靈活,但沒有固定流水線那麼直覺化。不過我們會保證以簡單代碼作爲開始。我們會使用類似於NeHe對OpenGL 1.x的教程那樣的方式,通過例子和教程來更好地理解可編程流水線背後的理念。

剛開始時頂點數組和着色器的使用看似十分痛苦,尤其在和舊式立即模式(immediate mode)和固定着色流水線進行對比時。[1]然而到了最後時——尤其當你在使用緩衝器對象時——你的代碼會乾淨更甚而且圖像會更快

本頁的代碼示例均在共有領域下。可以盡情對它們做你想做的事。

分享該文檔給你的朋友!維基教科書應當得到更多認同和貢獻 :)

注意:

  • 在某種程度上,的確可以混合固定流水線和可編程流水線,但是固定流水線已被不贊成使用,並且在OpenGL ES 2.0(或其WebGL衍生品)中完全不可用,所以我們不打算使用它。
  • 現在已經有了OpenGL 3和4——值得提到的是它們引入了幾何着色器——但和之前相比,這只是一個輕微的進化。而且它在移動平臺上不可用(2012年),所以我們暫時繼續專注於OpenGL 2.0。

基礎庫

[編輯]

示例代碼

[編輯]

顯示一個2D的三角形

[編輯]

Makefile

[編輯]

GNU/Linux或MinGW

[編輯]

MacOS

[編輯]

其他系統

[編輯]

初始化

[編輯]

我們來創建文件triangle.cpp

/* Using standard C++ output libraries */
#include <cstdlib>
#include <iostream>
using namespace std;

/* Use glew.h instead of gl.h to get all the GL prototypes declared */
#include <GL/glew.h>
/* Using SDL2 for the base window and OpenGL context init */
#include <SDL.h>
/* ADD GLOBAL VARIABLES HERE LATER */

bool init_resources(void) {
  /* FILLED IN LATER */
  return true;
}

void render(SDL_Window* window) {
  /* FILLED IN LATER */
}

void free_resources() {
  /* FILLED IN LATER */
}

void mainLoop(SDL_Window* window) {
	while (true) {
		SDL_Event ev;
		while (SDL_PollEvent(&ev)) {
			if (ev.type == SDL_QUIT)
				return;
		}
		render(window);
	}
}

int main(int argc, char* argv[]) {
	/* SDL-related initialising functions */
	SDL_Init(SDL_INIT_VIDEO);
	SDL_Window* window = SDL_CreateWindow("My First Triangle",
		SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
		640, 480,
		SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL);
	SDL_GL_CreateContext(window);

	/* Extension wrangler initialising */
	GLenum glew_status = glewInit();
	if (glew_status != GLEW_OK) {
		cerr << "Error: glewInit: " << glewGetErrorString(glew_status) << endl;
		return EXIT_FAILURE;
	}

	/* When all init functions run without errors,
	   the program can initialise the resources */
	if (!init_resources())
		return EXIT_FAILURE;

	/* We can display something if everything goes OK */
	mainLoop(window);

	/* If the program exits in the usual way,
	   free resources and exit with a success */
	free_resources();
	return EXIT_SUCCESS;
}

init_resources中,我們會創建我們的GLSL程序。 在render中,我們會繪製三角形。 在free_resources中,我們會銷毀該GLSL程序。

頂點數組

[編輯]

我們的首個三角形會以2D顯示——稍後我們將會轉到一些更複雜的東西上。 我們用3個點的2D (x,y)坐標來描述該三角形。 默認情況下,OpenGL的坐標都在[-1, 1]範圍內。

	GLfloat triangle_vertices[] = {
	    0.0,  0.8,
	   -0.8, -0.8,
	    0.8, -0.8,
	};

現在,僅僅需要將該數據結構保持在腦海中——稍後我們將會把它寫到代碼中。

注意:坐標都在-1和+1之間,但我們的窗口不是方形的!在下一課中,我們會看到如何修復該外觀比例(aspect ratio)

頂點着色器

[編輯]

區片着色器

[編輯]

GLSL程序

[編輯]

將三角形頂點傳給頂點着色器

[編輯]

我們提到過:我們應該把三角形的每個頂點都傳給頂點着色器——使用coord2d屬性。 這裏將展示如何在C代碼中聲明它。

首先,我們創建另一個全局變量:

GLint attribute_coord2d;

用下面的代碼來結束我們的init_resources過程:

	const char* attribute_name = "coord2d";
	attribute_coord2d = glGetAttribLocation(program, attribute_name);
	if (attribute_coord2d == -1) {
		cerr << "Could not bind attribute " << attribute_name << endl;
		return false;
	}

	return true;
}

現在,我們將三角形的頂點傳給頂點着色器。 一起動手寫render過程吧。每一節都在註釋中進行了解釋:

void render(SDL_Window* window) {
	/* Clear the background as white */
	glClearColor(1.0, 1.0, 1.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT);

	glUseProgram(program);
	glEnableVertexAttribArray(attribute_coord2d);
	GLfloat triangle_vertices[] = {
	    0.0,  0.8,
	   -0.8, -0.8,
	    0.8, -0.8,
	};
	/* Describe our vertices array to OpenGL (it can't guess its format automatically) */
	glVertexAttribPointer(
		attribute_coord2d, // attribute
		2,                 // number of elements per vertex, here (x,y)
		GL_FLOAT,          // the type of each element
		GL_FALSE,          // take our values as-is
		0,                 // no extra data between each position
		triangle_vertices  // pointer to the C array
						  );
	
	/* Push each element in buffer_vertices to the vertex shader */
	glDrawArrays(GL_TRIANGLES, 0, 3);
	
	glDisableVertexAttribArray(attribute_coord2d);

	/* Display the result */
	SDL_GL_SwapWindow(window);
}

glVertexAttribPointer告訴OpenGL去創建在init_resources中的數據緩衝區獲取每個頂點,並將它傳給頂點着色器。這些頂點定義了每個點在屏幕上的位置,並組成一個三角形——其像素使用區片着色器上色。

注意:在下一個教程中,我們會介紹頂點緩衝對象(Vertex Buffer Objects)——一種略複雜且更新的在顯示卡中存儲頂點的方式。

唯一剩下的部分是free_resources——用來在我們退出程序時做些清理工作。 在本例中它並不致命,但用這種方式來組織應用程序是很好的做法:

void free_resources() {
	glDeleteProgram(program);
}

我們的第一個OpenGL 2.0程序完成了!

如果它失敗了

[編輯]

測試吧!

[編輯]

備註

[編輯]
  1. 因爲非常多3D特性在OpenGL 2中被移除,一些人甚是有趣地將它定義爲一個2D光柵引擎