在主机端接口方面,首先,必须用<OpenGL/gl3.h>来代替原来的<OpenGL/gl.h>,这点很重要!
其次,在NSOpenGLPixelFormatAttribute对象中必须增加NSOpenGLPFAOpenGLProfile以及NSOpenGLProfileVersion3_2Core这两个属性。
在最后完成绘制后,需要调用CGL库的CGLFlushDrawable接口将所渲染的图形结果显示到视图或窗口上。
在OpenGL接口使用方面,必须使用VAO和VBO来存放顶点数据。然后,所有与固定功能流水线相关的接口都被移出,无法使用。因此这些被移除的功能只能通过Shader来实现。
在GLSL方面,必须显式地加上GLSL的版本号,并且至少为140(表示1.40),在基于Sandy Bridge的Intel HD Graphics 3000上默认为150(1.50)。举扮然后,in、out代替了原来的attribute和varying关键字。在Fragment shader中,gl_FragColor内建变量被取消,你可以自己定义一个out变量作为最后像素的输出。
下面给出一个比较基本、简洁的代码样例:
//
// MyGLView.h
// OpenGLShaderBasic
//
// Created by Zenny Chen on 10/4/10.
// Copyright 2010 GreenGames Studio. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@interface MyGLView : NSOpenGLView
{
GLuint program
GLuint vbo, vao
}
- (void)render
@end
//
// MyGLView.m
// OpenGLShaderBasic
//
//胡友 Created by Zenny Chen on 10/4/10.
// Copyright 2010 GreenGames Studio. All rights reserved.
//
#import "MyGLView.h"
// 这里必须注意!<gl3.h>头文裤答槐件必须被包含并取代<gl.h>,否则VAO接口会调用不正常,从而无法正确显示图形!
#import <OpenGL/gl3.h>
#define MY_PIXEL_WIDTH 128
#define MY_PIXEL_HEIGHT 128
@implementation MyGLView
// attribute index
enum
{
ATTRIB_VERTEX,
NUM_ATTRIBUTES
}
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file
{
GLint status
const GLchar *source
source = (GLchar *)[[NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil] UTF8String]
if (!source)
{
NSLog(@"Failed to load vertex shader")
return FALSE
}
*shader = glCreateShader(type)
glShaderSource(*shader, 1, &source, NULL)
glCompileShader(*shader)
GLint logLength
glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength)
if (logLength >0)
{
GLchar *log = (GLchar *)malloc(logLength)
glGetShaderInfoLog(*shader, logLength, &logLength, log)
NSLog(@"Shader compile log:\n%s", log)
free(log)
}
glGetShaderiv(*shader, GL_COMPILE_STATUS, &status)
if (status == 0)
{
glDeleteShader(*shader)
return FALSE
}
return TRUE
}
- (BOOL)linkProgram:(GLuint)prog
{
GLint status
glLinkProgram(prog)
GLint logLength
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength)
if (logLength >0)
{
GLchar *log = (GLchar *)malloc(logLength)
glGetProgramInfoLog(prog, logLength, &logLength, log)
NSLog(@"Program link log:\n%s", log)
free(log)
}
glGetProgramiv(prog, GL_LINK_STATUS, &status)
if (status == 0)
return FALSE
return TRUE
}
- (BOOL)validateProgram:(GLuint)prog
{
GLint logLength, status
glValidateProgram(prog)
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength)
if (logLength >0)
{
GLchar *log = (GLchar *)malloc(logLength)
glGetProgramInfoLog(prog, logLength, &logLength, log)
NSLog(@"Program validate log:\n%s", log)
free(log)
}
glGetProgramiv(prog, GL_VALIDATE_STATUS, &status)
if (status == 0)
return FALSE
return TRUE
}
- (BOOL)loadShaders
{
GLuint vertShader, fragShader
NSString *vertShaderPathname, *fragShaderPathname
// create shader program
program = glCreateProgram()
// bind attribute locations
// this needs to be done prior to linking
glBindAttribLocation(program, ATTRIB_VERTEX, "inPos")
// create and compile vertex shader
vertShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"vsh"]
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER file:vertShaderPathname])
{
NSLog(@"Failed to compile vertex shader")
return FALSE
}
// create and compile fragment shader
fragShaderPathname = [[NSBundle mainBundle] pathForResource:@"Shader" ofType:@"fsh"]
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:fragShaderPathname])
{
NSLog(@"Failed to compile fragment shader")
return FALSE
}
// attach vertex shader to program
glAttachShader(program, vertShader)
// attach fragment shader to program
glAttachShader(program, fragShader)
//glBindFragDataLocationEXT(program, 0, "myOutput")
// link program
if (![self linkProgram:program])
{
NSLog(@"Failed to link program: %d", program)
return FALSE
}
// release vertex and fragment shaders
if (vertShader)
glDeleteShader(vertShader)
if (fragShader)
glDeleteShader(fragShader)
return TRUE
}
- (id)initWithFrame:(NSRect)frameRect
{
self = [super initWithFrame:frameRect]
NSOpenGLPixelFormatAttribute attrs[] =
{
NSOpenGLPFADoubleBuffer,// 可选地,可以使用双缓冲
NSOpenGLPFAOpenGLProfile, // Must specify the 3.2 Core Profile to use OpenGL 3.2
NSOpenGLProfileVersion3_2Core,
0
}
NSOpenGLPixelFormat *pf = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]
if (!pf)
{
NSLog(@"No OpenGL pixel format")
}
NSOpenGLContext* context = [[NSOpenGLContext alloc] initWithFormat:pf shareContext:nil]
[self setPixelFormat:pf]
[pf release]
[self setOpenGLContext:context]
[context release]
return self
}
- (void)dealloc
{
if(vbo != 0)
glDeleteFramebuffers(1, &vbo)
if(vao != 0)
glDeleteRenderbuffers(1, &vao)
[super dealloc]
}
- (void)prepareOpenGL
{
[super prepareOpenGL]
[[self openGLContext] makeCurrentContext]
// Synchronize buffer swaps with vertical refresh rate
GLint swapInt = 1
[[self openGLContext] setValues:&swapInt forParameter:NSOpenGLCPSwapInterval]
// Drawing code here.
static const GLfloat squareVertices[] = {
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 0.0f, 1.0f
}
// In OpenGL 3.2 core profile, vertex buffer object and vertex array object must be used!
glGenVertexArrays(1, &vao)
glBindVertexArray(vao)
glGenBuffers(1, &vbo)
glBindBuffer(GL_ARRAY_BUFFER, vbo)
glBufferData(GL_ARRAY_BUFFER, sizeof(squareVertices), squareVertices, GL_STATIC_DRAW)
// Update attribute values
glEnableVertexAttribArray(ATTRIB_VERTEX)
glVertexAttribPointer(ATTRIB_VERTEX, 4, GL_FLOAT, 0, 0, (const GLvoid*)0)
// Load shaders and build the program
if(![self loadShaders])
return
// Use shader program
glUseProgram(program)
glViewport(0, 0, MY_PIXEL_WIDTH, MY_PIXEL_HEIGHT)
glClearColor(0.5f, 0.5f, 0.5f, 1.0f)
}
- (void)render
{
// render
glClear(GL_COLOR_BUFFER_BIT)
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
glFlush()
CGLFlushDrawable([[self openGLContext] CGLContextObj])
}
@end
以上两个文件是主要的使用OpenGL接口的样例头文件和源文件。在这个例子中,OpenGL的上下文基于一个视图(NSOpenGLView的子类)。
下面将给出Shader源代码:
//
// Shader.fsh
// GLSLTest
//
// Created by Zenny Chen on 4/11/10.
// Copyright GreenGames Studio 2010. All rights reserved.
//
// 在OpenGL3.2 Core Profile中,版本号必须显式地给出
#version 150
out vec4 myOutput
void main()
{
//gl_FragColor = vec4(0.1, 0.8, 0.5, 1.0)
myOutput = vec4(0.1, 0.8, 0.5, 1.0)
}
//
// Shader.vsh
// GLSLTest
//
// Created by Zenny Chen on 4/11/10.
// Copyright GreenGames Studio 2010. All rights reserved.
//
// 在OpenGL3.2 Core Profile中,版本号必须显式地给出
#version 150
in vec4 inPos
void main()
{
gl_Position = inPos
}
下面给出AppDelegate中对MyGLView对象的具体调用。
//
// AppDelegate.h
// OpenGL4Compute
//
// Created by zenny_chen on 12-12-9.
// Copyright (c) 2012年 zenny_chen. All rights reserved.
//
#import <Cocoa/Cocoa.h>
@class MyGLView
@interface AppDelegate : NSObject <NSApplicationDelegate>
{
MyGLView *glComputeObj
}
@property (assign) IBOutlet NSWindow *window
@end
//
// AppDelegate.m
// OpenGL4Compute
//
// Created by zenny_chen on 12-12-9.
// Copyright (c) 2012年 zenny_chen. All rights reserved.
//
#import "AppDelegate.h"
#import "MyGLView.h"
@implementation AppDelegate
- (void)dealloc
{
[super dealloc]
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
NSView *baseView = self.window.contentView
CGSize viewSize = baseView.frame.size
NSButton *button = [[NSButton alloc] initWithFrame:CGRectMake(20.0f, viewSize.height - 60.0f, 60.0f, 30.0f)]
[button setButtonType:NSMomentaryPushInButton]
[button setBezelStyle:NSRoundedBezelStyle]
[button setTitle:@"Show"]
[button setTarget:self]
[button setAction:@selector(computeButtonTouched:)]
[baseView addSubview:button]
[button release]
NSLog(@"The view is: %@", [self.window.contentView class])
}
- (void)computeButtonTouched:(id)sender
{
if(glComputeObj == nil)
{
NSView *baseView = self.window.contentView
CGSize viewSize = baseView.frame.size
glComputeObj = [[MyGLView alloc] initWithFrame:CGRectMake((viewSize.width - 128.0f) * 0.5f, (viewSize.height - 128.0f) * 0.5f, 128.0f, 128.0f)]
[baseView addSubview:glComputeObj]
[glComputeObj release]
}
[glComputeObj performSelector:@selector(render) withObject:nil afterDelay:0.1]
}
@end
一、环境搭建指南书中的地址
http://www.opengl-redbook.com/
去这里打包下载OpenGL红宝书的示例代码,解压后是这样
虽然没有第一章的代码但第一章的内容好歹算一个完整的例子,网上各种环境搭建教程也都以第一章的代码为例,我们就拿过来直接用一下。
1、打开VS2015创建一个空的控制台项目,然后新建一个cpp文件,粘贴红宝书第一章的示例代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
///////////////////////////////////////////////////////////////////////
//
// triangles.cpp
//
///////////////////////////////////////////////////////////////////////
#include <iostream>
using namespace std
#include "vgl.h"
#include "LoadShaders.h"
enum VAO_IDs { Triangles, NumVAOs }
enum Buffer_IDs { ArrayBuffer, NumBuffers }
enum Attrib_IDs { vPosition = 0 }
GLuint VAOs[NumVAOs]
GLuint Buffers[NumBuffers]
const GLuint NumVertices = 6
/贺迹/---------------------------------------------------------------------
//
/顷樱/ init
//雀拍丛
void init(void)
{
glGenVertexArrays(NumVAOs, VAOs)
glBindVertexArray(VAOs[Triangles])
GLfloat vertices[NumVertices][2] = {
{ -0.90, -0.90 }, // Triangle 1
{ 0.85, -0.90 },
{ -0.90, 0.85 },
{ 0.90, -0.85 }, // Triangle 2
{ 0.90, 0.90 },
{ -0.85, 0.90 }
}
glGenBuffers(NumBuffers, Buffers)
glBindBuffer(GL_ARRAY_BUFFER, Buffers[ArrayBuffer])
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices),
vertices, GL_STATIC_DRAW)
ShaderInfo shaders[] = {
{ GL_VERTEX_SHADER, "triangles.vert" },
{ GL_FRAGMENT_SHADER, "triangles.frag" },
{ GL_NONE, NULL }
}
GLuint program = LoadShaders(shaders)
glUseProgram(program)
glVertexAttribPointer(vPosition, 2, GL_FLOAT,
GL_FALSE, 0, BUFFER_OFFSET(0))
glEnableVertexAttribArray(vPosition)
}
//---------------------------------------------------------------------
//
// display
//
void display(void)
{
glClear(GL_COLOR_BUFFER_BIT)
glBindVertexArray(VAOs[Triangles])
glDrawArrays(GL_TRIANGLES, 0, NumVertices)
glFlush()
}
//---------------------------------------------------------------------
//
// main
//
int main(int argc, char** argv)
{
glutInit(&argc, argv)
glutInitDisplayMode(GLUT_RGBA)
glutInitWindowSize(512, 512)
glutInitContextVersion(4, 3)
glutInitContextProfile(GLUT_CORE_PROFILE)
glutCreateWindow(argv[0])
glewExperimental = GL_TRUE
if (glewInit()) {
cerr <<"Unable to initialize GLEW ... exiting" <<endl
exit(EXIT_FAILURE)
}
init()
glutDisplayFunc(display)
glutMainLoop()
}
2、打开项目属性,在“VC++ Directories”选项卡中,将前面下载回来的红宝书示例代码中的include目录加入“Include Directories”,将lib目录加入“Library Directories”
3、打开项目属性,在“General”选项卡中,将“Platform Toolset”设置为VS2013
4、在项目管理器中右击Source Files的Filter,添加现有项,然后找到红宝书源码目录中的lib目录,添加LoadShaders.cpp文件
5、打开项目属性,在“Linker/Input”选项卡中,在“Ignore Specific Default Libraries”中添加libcmtd.lib
6、在项目目录中新建两个文本分别命名为triangles.vert和triangles.frag,也就是我们的顶点着色器和片段着色器啦
triangles.vert
1
2
3
4
5
6
7
#version 430 core
layout(location = 0) in vec4 vPosition
void
main()
{
gl_Position = vPosition
}
triangles.frag
1
2
3
4
5
6
7
#version 430 core
out vec4 fColor
void
main()
{
fColor = vec4(0.0, 0.0, 1.0, 1.0)
}
7、编译,运行,搞定
二、所以然
1、关于第一步
这里的示例代码其实比书中的多了一句
glewExperimental = GL_TRUE
书里的代码用到了GLEW库,GLEW可以跨平台支持OpenGL高级扩展特性,没有GLEW的话在Win平台是无法使用4.3的那些功能的。这句
代码是GLEW所有扩展的一个开关,需要在glewInit()之前执行。如果没有这一句,编译是可以通过的,但在运行时会崩溃。
2、关于第二步
主要目的是将示例代码中用到的GLUT和GLEW库的头文件和库文件引入项目目录。这一步没设置好的话编译无法通过,现象一般是无法打开freeglut相关库文件或者某些函数没有声明。
网上有些教程让你自己去下载并编译freeglut和glew,然后拷贝到各种系统目录和VS的目录,其实没用。所有用到的头文件和库文件在第八版示例代码include和lib目录中都有了,你只要正确引入项目中就不会有问题。
3、关于第三步
只有VS2015需要这一步
VS2015更改了很多标准库的链接方式和宏定义,而红宝书第八版中用到的freeglut库中用到了这些标准库和宏。所以如果报_sscanf或者__iob_func是无法解析的外部符号之类的错误,只需要把平台工具集改成旧的就行了。
4、关于第四步
没有这一项,LoadShaders中定义的函数会报无法解析的外部符号错误。
5、关于第五步
如果不忽略这个默认库,会报库冲突或者无法打开库文件的错误。
6、关于第六步
没着色器画个卵,运行漆黑一片。
7、双显卡电脑又躺q了
我折腾了半天运行CMD显示Unable to create OpenGL 4.3 context,尼玛双显卡电脑默认用核心显卡启动应用。打开NVidia控制面板-管理3D设置-全局设置-首选图形处理器,把独显设为默认就OK了。
http://www.cnblogs.com/gagugagu/p/5211452.html
在OpenGL中,任何事物都处于 3D 空间中,而屏幕和窗口却都是 2D 像素数组,这就导致了OpenGL大部分工作都是关于把3D坐标转变为适配你屏幕的2D像素,3D坐标转为2D坐标的处理过程是由OpenGL的 图形渲染管线 (指的是一堆原始图形数据途经一个输送管道,期间经过各种变化处理最终出现在屏幕的过程)管理的。
图形渲染管线接受一组3D坐标,然后把它们转变为你屏幕上的有色2D像素输出。图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡铅腊慧都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做 着色器 (Shader)。
下图是图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分。
首先,我们以数组的形式传递3个3D坐标作为图形渲染管线的输入,用来表示一个三角形,这个数组叫做顶点数据(Vertex Data);顶点数据是一系列顶点的集合。一个顶点(Vertex)是一个3D坐标的数据的集合。而顶点数据是用顶点属性(Vertex Attribute)表示的,它可以包含任何我们想用的数据。
图形渲染管线的第一个部分是 顶点着色器 (Vertex Shader),它把一个单独的顶点作为输入。顶点着色器主要的目的是把3D坐标转为另一种3D坐标(后面会解释),同时顶点着色器允许我们对顶点属性进行一些基本处理。
图元装配 (Primitive Assembly)阶段将顶点着色器输出的所有顶点作为输入(如果是GL_POINTS,那么就是一个顶点),并所有的点装配成指定图元的形状;本节例子中是一个三角形。
图元装配阶段的输出会传递给 几何着色器 (Geometry Shader)。几何着色器把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状。例子中,它生成了另一个三角形。
几何着色器的输出会被传入 光栅化阶段 (Rasterization Stage),这里它会把图元映射为最终屏幕上相应的像素,生成供片段着色器(Fragment Shader)使用的片段(Fragment)。在局盯片段着色器运行之前会执行 裁切 (Clipping)。裁切会丢弃超出你的视图以外的所有像素,用来提升执行效率。
片段着色器 的主要目的是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色。
在所有对应颜色值确定以后,最终的对象将会被传到最后一个阶段,我们叫做 Alpha测试 和 混合 (Blending)阶段。这个阶段检测片段的对应的深度(和模板(Stencil))值,用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。这个阶段也会检查 alpha 值(alpha值定义了一个物体的透明度)并对物体进行 混合 (Blend)。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同。
可以看到,图形渲染管线非常复杂,它包含很多可配置的部分。然而,对于大多数场合,我们只需要配置顶点和片段着色器就行了。几何着色器是可选的,通常使用它默认的着色器就行了。
在现代OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器(因为GPU中没有默认的顶点/片段着色器)。
OpenGL 首先接收用户提供的 几何数据 (顶点和几何图元),并且将它输入到一系列 着色器阶段 中进行处理,包括:顶点着色、细分着色(它本身包含两个着色器),以及最后的几何着色,然后它将被送槐答入 光栅化单元(rasterizer) 。光栅化单元负责对所有 剪切区域(clipping region) 内的图元生成 片元数据 ,然后对每个生成的片元都执行一个 片元着色器 。
没必要每次绘图时都复制顶点数据,而是在 图形内存中缓存 这些数据,这样可以显著改善渲染性能,也可以降低内存带宽和电力消耗需求。这就是 顶点缓冲区 对象发挥作用的地方
顶点数组对象 :Vertex Array Object,VAO 保存缓存以及顶点属性状态信息。
顶点缓冲对象 :Vertex Buffer Object,VBO 用于分配内存,保存顶点数据 给图形卡使用的一种缓存对象。
索引缓冲对象 :Element Buffer Object,EBO或Index Buffer Object,IBO 保存顶点索引的一种缓存对象。
在定义好顶点数据以后,需要在内存中存储这些顶点,我们通过 顶点缓冲对象(Vertex Buffer Objects, VBO) 管理这个内存,它会在GPU内存(通常被称为显存)中储存大量顶点。使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。从CPU把数据发送到显卡相对较慢,所以只要可能我们都要尝试尽量一次性发送尽可能多的数据。当数据发送至显卡的内存中后,顶点着色器几乎能立即访问顶点,这是个非常快的过程。
下面是一个生成顶点缓冲区的例子
glGenBuffers(GLsizei n, GLuint * buffers) :分配n个缓冲区对象名称,并在buffers中返回它们。
glBindBuffer 用于指定当前缓冲区对象。第一次通过调用
glBindBuffer 绑定缓冲区对象名称时,缓冲区对象可以默认状态分配;如果分配成功,则分配的对象绑定微目标的当前缓冲区对象。
glBufferData 用于创建和初始化顶点数组或元素数组。
简而言之,光栅化阶段绘制对应的图元(点、线、三角形),将 图元 转化为一组 二维数组 的过程,然后传递给 片元着色器 处理。这些二维数组代表屏幕上绘制的 像素
片元着色器主要是对 光栅化 处理后生成的片元 逐个进行 处理(并行)。接收顶点着色器输出的值,需要传入的数据,以及它经过变换矩阵后输出值存储位置。
关于混合可以参考 这篇文章
拓展阅读:
iOS 渲染原理解析
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)