天天看点

【OpenGL】混合、抗锯齿、多重采样

目录

混合和抗锯齿

混合案例

抗锯齿案例

多重采样【木有案例】

优化

混合和抗锯齿

混合案例

【OpenGL】混合、抗锯齿、多重采样
// Blending.cpp
// Move a Block based on arrow key movements,
// Blend it with background blocks

#pragma comment(lib, "gltools.lib")

#include <GLTools.h>	// OpenGL toolkit
#include <GLShaderManager.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLBatch	squareBatch;
GLBatch greenBatch;
GLBatch redBatch;
GLBatch blueBatch;
GLBatch blackBatch;

GLShaderManager	shaderManager;


GLfloat blockSize = 0.2f;
GLfloat vVerts[] = { -blockSize, -blockSize, 0.0f,
					  blockSize, -blockSize, 0.0f,
					  blockSize,  blockSize, 0.0f,
					 -blockSize,  blockSize, 0.0f };

///
// This function does any needed initialization on the rendering context. 
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
{
	// Black background
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

	shaderManager.InitializeStockShaders();

	// Load up a triangle fan
	squareBatch.Begin(GL_TRIANGLE_FAN, 4);
	squareBatch.CopyVertexData3f(vVerts);
	squareBatch.End();

	GLfloat vBlock[] = { 0.25f, 0.25f, 0.0f,
						 0.75f, 0.25f, 0.0f,
						 0.75f, 0.75f, 0.0f,
						 0.25f, 0.75f, 0.0f };

	greenBatch.Begin(GL_TRIANGLE_FAN, 4);
	greenBatch.CopyVertexData3f(vBlock);
	greenBatch.End();


	GLfloat vBlock2[] = { -0.75f, 0.25f, 0.0f,
						  -0.25f, 0.25f, 0.0f,
						  -0.25f, 0.75f, 0.0f,
						  -0.75f, 0.75f, 0.0f };

	redBatch.Begin(GL_TRIANGLE_FAN, 4);
	redBatch.CopyVertexData3f(vBlock2);
	redBatch.End();


	GLfloat vBlock3[] = { -0.75f, -0.75f, 0.0f,
						-0.25f, -0.75f, 0.0f,
						-0.25f, -0.25f, 0.0f,
						-0.75f, -0.25f, 0.0f };

	blueBatch.Begin(GL_TRIANGLE_FAN, 4);
	blueBatch.CopyVertexData3f(vBlock3);
	blueBatch.End();


	GLfloat vBlock4[] = { 0.25f, -0.75f, 0.0f,
						0.75f, -0.75f, 0.0f,
						0.75f, -0.25f, 0.0f,
						0.25f, -0.25f, 0.0f };

	blackBatch.Begin(GL_TRIANGLE_FAN, 4);
	blackBatch.CopyVertexData3f(vBlock4);
	blackBatch.End();
}

// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
{
	GLfloat stepSize = 0.025f;

	GLfloat blockX = vVerts[0];   // Upper left X
	GLfloat blockY = vVerts[7];  // Upper left Y

	if (key == GLUT_KEY_UP)
		blockY += stepSize;

	if (key == GLUT_KEY_DOWN)
		blockY -= stepSize;

	if (key == GLUT_KEY_LEFT)
		blockX -= stepSize;

	if (key == GLUT_KEY_RIGHT)
		blockX += stepSize;

	// Collision detection
	if (blockX < -1.0f) blockX = -1.0f;
	if (blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;;
	if (blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;
	if (blockY > 1.0f) blockY = 1.0f;

	// Recalculate vertex positions
	vVerts[0] = blockX;
	vVerts[1] = blockY - blockSize * 2;

	vVerts[3] = blockX + blockSize * 2;
	vVerts[4] = blockY - blockSize * 2;

	vVerts[6] = blockX + blockSize * 2;
	vVerts[7] = blockY;

	vVerts[9] = blockX;
	vVerts[10] = blockY;

	squareBatch.CopyVertexData3f(vVerts);

	glutPostRedisplay();
}





///
// Called to draw scene
void RenderScene(void)
{
	// Clear the window with current clearing color
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
	GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
	GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 1.0f };
	GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };


	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
	greenBatch.Draw();

	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
	redBatch.Draw();

	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
	blueBatch.Draw();

	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
	blackBatch.Draw();



	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
	squareBatch.Draw();
	glDisable(GL_BLEND);


	// Flush drawing commands
	glutSwapBuffers();
}



///
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
{
	glViewport(0, 0, w, h);
}

///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
	gltSetWorkingDirectory(argv[0]);

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Move Block with Arrow Keys to see blending");

	GLenum err = glewInit();
	if (GLEW_OK != err)
	{
		// Problem: glewInit failed, something is seriously wrong.
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
		return 1;
	}

	glutReshapeFunc(ChangeSize);
	glutDisplayFunc(RenderScene);
	glutSpecialFunc(SpecialKeys);

	SetupRC();

	glutMainLoop();
	return 0;
}
           

抗锯齿案例

【OpenGL】混合、抗锯齿、多重采样
【OpenGL】混合、抗锯齿、多重采样
// Blending.cpp
// Move a Block based on arrow key movements,
// Blend it with background blocks

#include <GLTools.h>	// OpenGL toolkit
#include <GLShaderManager.h>

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLBatch	squareBatch;
GLBatch greenBatch;
GLBatch redBatch;
GLBatch blueBatch;
GLBatch blackBatch;

GLShaderManager	shaderManager;


GLfloat blockSize = 0.2f;
GLfloat vVerts[] = { -blockSize, -blockSize, 0.0f, 
	                  blockSize, -blockSize, 0.0f,
					  blockSize,  blockSize, 0.0f,
					 -blockSize,  blockSize, 0.0f};

///
// This function does any needed initialization on the rendering context. 
// This is the first opportunity to do any OpenGL related tasks.
void SetupRC()
	{
	// Black background
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f );
    
	shaderManager.InitializeStockShaders();

	// Load up a triangle fan
	squareBatch.Begin(GL_TRIANGLE_FAN, 4);
	squareBatch.CopyVertexData3f(vVerts);
	squareBatch.End();

    GLfloat vBlock[] = { 0.25f, 0.25f, 0.0f,
                         0.75f, 0.25f, 0.0f,
                         0.75f, 0.75f, 0.0f,
                         0.25f, 0.75f, 0.0f};
    
    greenBatch.Begin(GL_TRIANGLE_FAN, 4);
    greenBatch.CopyVertexData3f(vBlock);
    greenBatch.End();
    

    GLfloat vBlock2[] = { -0.75f, 0.25f, 0.0f,
                          -0.25f, 0.25f, 0.0f,
                          -0.25f, 0.75f, 0.0f,
                          -0.75f, 0.75f, 0.0f};
        
    redBatch.Begin(GL_TRIANGLE_FAN, 4);
    redBatch.CopyVertexData3f(vBlock2);
    redBatch.End();
    

    GLfloat vBlock3[] = { -0.75f, -0.75f, 0.0f,
                        -0.25f, -0.75f, 0.0f,
                        -0.25f, -0.25f, 0.0f,
                        -0.75f, -0.25f, 0.0f};
        
    blueBatch.Begin(GL_TRIANGLE_FAN, 4);
    blueBatch.CopyVertexData3f(vBlock3);
    blueBatch.End();


    GLfloat vBlock4[] = { 0.25f, -0.75f, 0.0f,
                        0.75f, -0.75f, 0.0f,
                        0.75f, -0.25f, 0.0f,
                        0.25f, -0.25f, 0.0f};
        
    blackBatch.Begin(GL_TRIANGLE_FAN, 4);
    blackBatch.CopyVertexData3f(vBlock4);
    blackBatch.End();
	}

// Respond to arrow keys by moving the camera frame of reference
void SpecialKeys(int key, int x, int y)
    {
	GLfloat stepSize = 0.025f;

	GLfloat blockX = vVerts[0];   // Upper left X
	GLfloat blockY = vVerts[7];  // Upper left Y

	if(key == GLUT_KEY_UP)
		blockY += stepSize;

	if(key == GLUT_KEY_DOWN)
		blockY -= stepSize;
	
	if(key == GLUT_KEY_LEFT)
		blockX -= stepSize;

	if(key == GLUT_KEY_RIGHT)
		blockX += stepSize;

	// Collision detection
	if(blockX < -1.0f) blockX = -1.0f;
	if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;;
	if(blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;
	if(blockY > 1.0f) blockY = 1.0f;

	// Recalculate vertex positions
	vVerts[0] = blockX;
	vVerts[1] = blockY - blockSize*2;
	
	vVerts[3] = blockX + blockSize*2;
	vVerts[4] = blockY - blockSize*2;
	
	vVerts[6] = blockX + blockSize*2;
	vVerts[7] = blockY;

	vVerts[9] = blockX;
	vVerts[10] = blockY;

	squareBatch.CopyVertexData3f(vVerts);

	glutPostRedisplay();
	}





///
// Called to draw scene
void RenderScene(void)
	{
	// Clear the window with current clearing color
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

	GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 1.0f };
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };


    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
    greenBatch.Draw();

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    redBatch.Draw();

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    blueBatch.Draw();

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
    blackBatch.Draw();



    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    squareBatch.Draw();
    glDisable(GL_BLEND);
    

	// Flush drawing commands
	glutSwapBuffers();
	}



///
// Window has changed size, or has just been created. In either case, we need
// to use the window dimensions to set the viewport and the projection matrix.
void ChangeSize(int w, int h)
	{
	glViewport(0, 0, w, h);
	}

///
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
	{
	gltSetWorkingDirectory(argv[0]);
	
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Move Block with Arrow Keys to see blending");
	
	GLenum err = glewInit();
	if (GLEW_OK != err)
		{
		// Problem: glewInit failed, something is seriously wrong.
		fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
		return 1;
		}
	
	glutReshapeFunc(ChangeSize);
	glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);

	SetupRC();

	glutMainLoop();
	return 0;
	}
           

抗锯齿是利用混合来混合周围像素达到抗锯齿目的,在任何图元的边缘上,像素颜色会稍微延伸到相邻的像素。

/
void DrawWireFramedBatch(GLBatch* pBatch)
{
	// Draw the batch solid green
	shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
	pBatch->Draw();

	// Draw black outline
	glPolygonOffset(-1.0f, -1.0f);      // Shift depth values
	glEnable(GL_POLYGON_OFFSET_LINE);

	// Draw lines antialiased
	glEnable(GL_LINE_SMOOTH);  //开启线段抗锯齿
	glEnable(GL_BLEND);        //开启混合
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); //混合因子设置

	// Draw black wireframe version of geometry
	glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);  
	glLineWidth(2.5f);
	shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
	pBatch->Draw();

	// Put everything back the way we found it
	glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	glDisable(GL_POLYGON_OFFSET_LINE);
	glLineWidth(1.0f);
	glDisable(GL_BLEND);        //关闭混合
	glDisable(GL_LINE_SMOOTH);  //关闭抗锯齿线段
}
           

带注释的就是主要解释代码内容。

抗锯齿还有GL_POINT_SMOOTH、GL_POLYGONE_SMOOTH 对应点抗锯齿和多变现抗锯齿,但多边形抗锯齿往往不好用,因此多重采样来了。

设定抗锯齿算法(GL_NICEST 最好的):glHint(GL_POINT_SMOOTH_HINT, GL_NICEST) ,  此外还有GL_LINE_SMOOTH_HINT、GL_POLYGONE_SMOOTH_HINT,  以及 GL_FASTEST 最快速度抗锯齿算法。

多重采样【木有案例】

书上解释说是  点和线的抗锯齿是支持的比较好的,但多边形的平滑抗锯齿处理就支持的不是很好,原因是抗锯齿处理是基于混合操作的,这就需要从前到后对所有的图元进行排序,这是非常麻烦的!

问题来了! 为什么要从前到后排序图元呢?因为开启了混合!在学习UnityShader时我们已经认识到 透明队列 就是开启混合的,所以它能透明嘛,而透明队列 必然会进行 从前到后 渲染。 (前是指摄像机更远的地方,后是指离摄像机更近的地方, 而这个排序仅仅只是物体个体的排序,而不是像素级别的排序,如果是像素级别的排序的话 那就不会有深度测试这么一个玩意了。因为很难做到 像素级的排序,因为做不到 从而出现各种奇怪的问题 ,比如 2个互相穿透的半透明物体,怎么处理这些物体的抗锯齿?可能这时就出现问题了,比如 本应该在后渲染的片段 变成了 在前面渲染了, 就会导致 抗锯齿混合时 并没有正确混合到周围那些像素, 因为那些像素还没被渲染出来。。。(大概是这个意思)

OpenGL 1.3特性 多重采样技术,需要额外增多一个缓冲区“多重采样缓冲区”。   

所有图元在每个像素上都进行了多次采样,其结果就存储在这个缓冲区中。每次当这个像素进行更新时,这些采样值进行解析,以产生一个单独的值。从程序员角度看,它是自动的,属于“幕后发生的事情”。

这种处理会带来额外的内存和处理器开销,有可能对性能造成影响。有些OpengGL实现可能不支持多渲染环境中的多重采样。

多重采样我个人理解与抗锯齿差别在于 ,抗锯齿是混合,必须开启混合,而多重采样我认为它不是混合了,而是融合更恰当,类似于 把周围9个像素的颜色 相加 取平均 这种事情,可能会比单纯的混合更加简单地处理抗锯齿问题。

代码上:glutInitDisplayMode(GLUT_MULTISAMPLE | GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); 开启多重采样缓冲区 、双缓存区、 颜色缓冲区、 深度缓冲区。

glEnable(GL_MULTISAMPLE) 开启多重采样  ,此时会忽略点、直线、多边形的平滑特性 即 glEnable(GL_POINT_SMOOTH) 那些会不生效了。

因为多重采样比抗锯齿混合处理更耗,基本上绘制点和线时 都禁用多重采样,开启点和线的抗锯齿SMOOTH混合处理,而绘制多边形时开启多重采样。

下面内容有点懵逼

多重采样缓冲区在默认情况下使用片段的RGB值,并不包括Alpha。可以通过glEnable(下面3个值之一)来修改这个行为

GL_SAMPLE_ALPHA_TO_COVERAGE   使用alpha值

GL_SAMPLE_ALPHA_TO_ON 使用alpha值并设置为1使用它

GL_SAMPLE_COVERAGE 使用glSampleCoverage函数所设置的值

当启动glEnable(GL_SAMPLE_COVERAGE)时  可用 glSampleCoverage(GLclampf value , GLboolean invert);设定特定值,它是与片段覆盖值进行按位于操作的结果

Glclampf是[0,32]位的浮点数

优化

glEnable 开启 和 glDisable 关闭状态 都会对渲染的性能造成影响,可以将那些相同状态的渲染几何图形代码放在一起,避免不必要的glEnable和glDisbale。