精华内容
参与话题
问答
  • OpenGL进阶(八) - GLSL入门

    千次阅读 2013-07-31 11:08:34
    简介  随着图形硬件的发展,渲染管线由固定... 今天要介绍的就是和OpenGL结合非常紧密的GLSL(OpenGL Shading Language). 通过OpenGL的API我们可以绘制图元,变换图形等等,当并不能改变基础的渲染管线。在OpenGL中

    简介

            随着图形硬件的发展,渲染管线由固定不可更改想着可编程和更平滑的方向不断发展。越来越多的基于GPU的编程语言开始出现,cg,cuda,各种着色语言等等。

            今天要介绍的就是和OpenGL结合非常紧密的GLSL(OpenGL Shading Language). 通过OpenGL的API我们可以绘制图元,变换图形等等,当并不能改变基础的渲染管线。在OpenGL中使用GLSL,就能将渲染管线中固定的功能阶段转变成可编程的。

            编程环境:Ubuntu12.04 32bit  GTX480

    GLSL简介

             OpenGL着色语言(GLSL――OpenGL Shading Language)是用来在OpenGL中着色编程的语言,也即开发人员写的短小的自定义程序,他们是在图形卡的GPU (Graphic Processor Unit图形处理单元)上执行的,代替了固定的渲染管线的一部分。比如:视图转换、投影转换等。GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片断着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL 中的状态,GLSL内置变量进行传递。它拥有一下的一些特点:

    1.是一种高级的过程式语言;

    2.作为OpenGL标准的一个部分,也就意味着开源,跨平台;

    3.基于C和C++的语法和流程控制;

    4.天然支持向量和矩阵的运算;

    5.比C和C++更加严格的变量控制;

    6.使用变量来处理输入和输出而不是读写文档;

    7.Shader的长度并没有限制,也没有必要去查询。

         为什要使用OpenGL shader?

    1.增加材料的真材实感 - 石头,草地,木头等等;

    2.增加光照效果的真材实感 - 面光源,软阴影等等;

    3.高级的渲染效果 - 全局照明,光线追踪等等;

    4.非真实的材质 - 模拟画笔效果,钢笔绘制效果等等;

    5.阶段贴图 - 动态生成2D和3D的纹理,而不是静态的图像;

    6.图像处理 - 卷积,遮罩,复杂混合等;

    7.动态效果 - 关键帧插值,粒子系统,动画;

    8.可编程反走样方法;

    9.通用计算 - 排序,数学建模,流体计算;

    这些特性在使用opengl的时候可能可以去实现,但是都会有些局限,而现在,通过shader,我们可以通过显卡的硬件加速来显著增加渲染的速度,同时可以解放CPU。


    写一个简单的Shader

    首先来看一下电脑的OpenGL环境,终端运行:

    glxinfo | grep OpenGL



    基于SDL的OpenGL已经安装好(参考这里:SDL入门学习),接下来需要安装一下OpenGL的扩展库。

    sudo apt-get install glew-utils libglew1.6


    这次先绘制一个简单的矩形。

    在工程文件夹下创建一个basic.vert,作为vertex shader.

    #version 400
    in vec3 VertexPosition;
    in vec3 VertexColor;
    out vec3 Color;
    void main()
    {
    	Color = VertexColor;
    	gl_Position = vec4( VertexPosition, 1.0);
    }

    ·in – for input parameters
    ·out – for outputs of the function. The returnstatement is also an option for sending the result of a function.
    ·inout – for parameters that are both input andoutput of a function (新版本的GLSL似乎已经废除)

    再创建一个basic.frag,作为fragment shader.

    #version 400
    void main(void)
    {
    	gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
    }

    创建main.c,代码如下:

    /*****************************************************************************
    Copyright: 2013, ustc All rights reserved.
    contact:k283228391@126.com
    File name: main.c
    Description:Using opengl shading language in SDL.
    Author:Silang Quan
    Version: 1.0
    Date: 2013.7.30
    *****************************************************************************/
    #include <SDL/SDL.h>
    #include <GL/glew.h>
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    
    const int SCREEN_WIDTH = 800;
    const int SCREEN_HEIGHT =800;
    const int SCREEN_BPP = 32;
    SDL_Surface *screen;
    //Whether the window is windowed or not
    bool windowed;
    //Whether the window is fine
    bool windowOK;
    //Handler for GLSL program
    GLuint programHandle;
    GLuint vShader;
    GLuint fShader;
    
    void quit( int code )
    {
        SDL_Quit( );
        /* Exit program. */
        exit( code );
    }
    
    char *textFileRead(char *fn) {
     
        FILE *fp;
        char *content = NULL;
     
        int count=0;
     
        if (fn != NULL) {
            fp = fopen(fn,"rt");
     
            if (fp != NULL) {
     
          fseek(fp, 0, SEEK_END);
          count = ftell(fp);
          rewind(fp);
     
                if (count > 0) {
                    content = (char *)malloc(sizeof(char) * (count+1));
                    count = fread(content,sizeof(char),count,fp);
                    content[count] = '\0';
                }
                fclose(fp);
            }
        }
        return content;
    }
    
    void toggle_fullscreen()
    {
    	//If the screen is windowed
    	if( windowed == true )
    	{
    		//Set the screen to fullscreen
    		screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL|SDL_RESIZABLE| SDL_FULLSCREEN );
    
    		//If there's an error
    		if( screen == NULL )
    		{
    			windowOK = false;
    			return;
    		}
    
    		//Set the window state flag
    		windowed = false;
    	}
    	//If the screen is fullscreen
    	else if( windowed == false )
    	{
    		//Window the screen
    		screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_OPENGL|SDL_RESIZABLE );
    
    		//If there's an error
    		if( screen == NULL )
    		{
    			windowOK = false;
    			return;
    		}
    
    		//Set the window state flag
    		windowed = true;
    	}
    }
    
    void handleKeyEvent( SDL_keysym* keysym )
    {
        switch( keysym->sym )
    	{
        case SDLK_ESCAPE:
            quit( 0 );
            break;
        case SDLK_SPACE:
            break;
        case SDLK_F1:
    		toggle_fullscreen();
    		break;
        default:
            break;
        }
    }
    
    void resizeGL(int width,int height)
    {
        if ( height == 0 )
        {
            height = 1;
        }
        //Reset View
        glViewport( 0, 0, (GLint)width, (GLint)height );
        //Choose the Matrix mode
        glMatrixMode( GL_PROJECTION );
        //reset projection
        glLoadIdentity();
        //set perspection
        gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );
        //choose Matrix mode
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity();
    }
    
    void handleEvents()
    {
        // Our SDL event placeholder.
        SDL_Event event;
        //Grab all the events off the queue.
        while( SDL_PollEvent( &event ) ) {
            switch( event.type ) {
            case SDL_KEYDOWN:
                // Handle key Event
                handleKeyEvent( &event.key.keysym );
                break;
            case SDL_QUIT:
                // Handle quit requests (like Ctrl-c).
                quit( 0 );
                break;
            case SDL_VIDEORESIZE:
    			//Handle resize event
                screen = SDL_SetVideoMode(event.resize.w, event.resize.h, 16,
                                          SDL_OPENGL|SDL_RESIZABLE);
                if ( screen )
                {
                    resizeGL(screen->w, screen->h);
                }
                break;
            }
        }
    }
    
    void initSDL(int width,int height,int bpp,int flags)
    {
        // First, initialize SDL's video subsystem.
        if( SDL_Init( SDL_INIT_VIDEO ) < 0 )
        {
            fprintf( stderr, "Video initialization failed: %s\n",
                     SDL_GetError( ) );
            quit( 1 );
        }
        atexit(SDL_Quit);
    	//Set some Attribute of OpenGL in SDL
        SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, 16 );
        SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    
        //Set the video mode
        screen= SDL_SetVideoMode( width, height, bpp,flags);
        if(!screen )
        {
            fprintf( stderr, "Video mode set failed: %s\n",SDL_GetError( ) );
            quit( 1 );
        windowed=false;
    	}
    	else windowed=true;
        resizeGL(screen->w, screen->h);
        //Set caption
        SDL_WM_SetCaption( "OpenGL Shading Language Test", NULL );
        
    
    }
    
    void initShader()
    {
    	
    	vShader = glCreateShader( GL_VERTEX_SHADER );
    	fShader = glCreateShader( GL_FRAGMENT_SHADER );
    	printf("Here\n");
    	if(0 == vShader || 0 == fShader)
    	{
    		fprintf(stderr, "Error creating vertex shader.\n");
    		quit(1);
    	}
    	
    	GLchar* vShaderCode = textFileRead("basic.vert");
    	GLchar* fShaderCode = textFileRead("basic.frag");
    	const GLchar* vCodeArray[1] = {vShaderCode};
    	const GLchar* fCodeArray[1] = {fShaderCode};
    	glShaderSource(vShader, 1, vCodeArray, NULL);
    	glShaderSource(fShader, 1, fCodeArray, NULL);
    	
    	glCompileShader(vShader);
    	glCompileShader(fShader);
    	free(vShaderCode);
    	free(fShaderCode);
    	//const GLchar* codeArray[] = {shaderCode};
    	//Check the compile result
    	GLint logLen;
    	glGetShaderiv(vShader, GL_INFO_LOG_LENGTH, &logLen);
    	if(logLen > 0)
    	{
    		char *log = (char *)malloc(logLen);
    		GLsizei written;
    		glGetShaderInfoLog(vShader, logLen, &written, log);
    		printf("Shader compile error log: %s\n",log);
    		free(log);
    	}
    	
    	programHandle = glCreateProgram();
    	if(0 == programHandle)
    	{
    		fprintf(stderr, "Error creating programHandle.\n");
    		quit(1);
    	}
    	
    	glAttachShader(programHandle, vShader);
    	glAttachShader(programHandle, fShader);
    	glLinkProgram(programHandle);
    	//glUseProgram(programHandle);
    }
    
    void freeShader()
    {
    	glDetachShader(programHandle, fShader);
    	glDetachShader(programHandle, vShader);
    	glDeleteShader(fShader);
    	glDeleteShader(vShader);
    	//glDetachShader(fShader);
    	//glDetachShader(vShader);
    	//glDetachShader(programHandle);
    }
    void renderGL()
    {
    	/* These are to calculate our fps */
        static GLint T0     = 0;
    	static GLint Frames = 0;
        // Clear the color and depth buffers.
        glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
        // We don't want to modify the projection matrix. */
        glMatrixMode( GL_MODELVIEW );
        glLoadIdentity( );
        // Move down the z-axis.
        glTranslatef( 0.0, 0.0, -5.0 );
    	//Draw a square
        glUseProgram(programHandle);
        glBegin(GL_QUADS);
                glVertex2f(-0.5f, -0.5f);
                glVertex2f( 0.5f, -0.5f);
                glVertex2f( 0.5f,  0.5f);
                glVertex2f(-0.5f,  0.5f);
        glEnd();
            // Unbind shader
        glUseProgram(0);
        SDL_GL_SwapBuffers( );
        
        /* Gather our frames per second */
        Frames++;
        {
    	GLint t = SDL_GetTicks();
    	if (t - T0 >= 5000) {
    	    GLfloat seconds = (t - T0) / 1000.0;
    	    GLfloat fps = Frames / seconds;
    	    printf("%d frames in %g seconds = %g FPS\n", Frames, seconds, fps);
    	    T0 = t;
    	    Frames = 0;
    	}
        }
    }
    
    void initGL( int width, int height )
    {
        float ratio = (float) width / (float) height;
        // Our shading model--Gouraud (smooth).
        glShadeModel( GL_SMOOTH );
        // Set the clear color.
        glClearColor( 0, 0, 0, 0 );
        // Setup our viewport.
        glViewport( 0, 0, width, height );
        //Change to the projection matrix and set our viewing volume.
        glMatrixMode( GL_PROJECTION );
        glLoadIdentity();
        gluPerspective( 60.0, ratio, 1.0, 100.0 );
    }
    
    int main( int argc, char* argv[] )
    {
    
    	// Color depth in bits of our window.
    	int flags= SDL_OPENGL|SDL_RESIZABLE;
    	//Set the SDL
    	initSDL(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,flags);
    	if(glewInit() != GLEW_OK) exit(EXIT_FAILURE);
    	//Init vertext shader
    	initShader();
    	//Set the OpenGL
    	initGL(SCREEN_WIDTH, SCREEN_HEIGHT );
    
        //main loop
        while(true)
    	{
            /* Process incoming events. */
            handleEvents( );
            /* Draw the screen. */
            renderGL( );
        }
        // Free Shader
        freeShader();
        return 0;
    }
    

    主要是增加了几个关于Shader的函数,initShader用于shader的初始化,freeShader用于删除shader,释放内存。使用shader之前还需要调用glewInit来初始化glew。


    终端编译命令:

    g++ main.c -o main -lSDL -lGL -lGLU -lGLEW



    解释一下几个相关的API。

    GLuint glCreateShader(GLenum shaderType);
    Parameter:
    
    shaderType – GL_VERTEX_SHADER, GL_GEOMETRY_SHADER, GL_TESS_CONTROL_SHADER, GL_TESS_EVALUATION_SHADER, or GL_FRAGMENT_SHADER.
    Return Value:
    
    the shader handler

    void glShaderSource(GLuint shader, int numOfStrings, const char **strings, int *lengthOfStrings);
    Parameters:
    
    shader – the handler to the shader.
    numOfStrings – the number of strings in the array.
    strings – the array of strings.
    lengthOfStrings – an array with the length of each string, or NULL, meaning that the strings are NULL terminated.

    void glCompileShader(GLuint shader);
    Parameters:
    
    shader – the handler to the shader.


    void glUseProgram(GLuint program);
     Installs a program object as part of current rendering state
    Parameters:
    program
    Specifies the handle of the program object whose executables are to be used as part of current rendering state.


    ...

    整个opengl程序执行的流程如下:


    更多函数参考OpenGL reference - http://www.opengl.org/sdk/docs/man/


    参考

    OpenGL 4.0 Shading Language Cookbook

    GLSL Core Tutorial – Creating a Shader - http://www.lighthouse3d.com/tutorials/glsl-core-tutorial/creating-a-shader/

    Hello GLSL - http://sindney.com/blog/posts/hello-glsl/

    OpenGL reference - http://www.opengl.org/sdk/docs/man/


    展开全文
  • GLSL教程】(一)图形流水线

    万次阅读 多人点赞 2011-07-08 22:07:31
    这是一些列来自lighthouse3d的GLSL教程,非常适合入门。我将边学习边翻译该教程的内容,同时记录在这里,方便以后查询。流水线概述下图描述了一个简化的图形处理流水线,虽然简略但仍然可以展示着色器编程(shader ...
    这是一些列来自lighthouse3d的GLSL教程,非常适合入门。我将边学习边翻译该教程的内容,同时记录在这里,方便以后查询。


    流水线概述

    下图描述了一个简化的图形处理流水线,虽然简略但仍然可以展示着色器编程(shader programming)的一些重要概念。


    一个固定流水线包括如下功能:

    顶点变换(Vertex Transformation)

    这里一个顶点是一个信息集合,包括空间中的位置、顶点的颜色、法线、纹理坐标等。这一阶段的输入是独立的顶点信息,固定功能流水线在这一阶段通常进行如下工作:

    ·顶点位置变换

    ·为每个顶点计算光照

    ·纹理坐标的生成与变换

    图元组合和光栅化(Primitive Assembly and Rasterization)

    此阶段的输入是变换后的顶点和连接信息(connectivity information)。连接信息告诉流水线顶点如何组成图元(三角形、四边形等)。此阶段还负责视景体(view frustum)裁剪和背面剔除。

    光栅化决定了片断(fragment),以及图元的像素位置。这里的片断是指一块数据,用来更新帧缓存(frame buffer)中特定位置的一个像素。一个片断除了包含颜色,还有法线和纹理坐标等属性,这些信息用来计算新的像素颜色值。

    本阶段的输出包括:

    ·帧缓存中片断的位置

    ·在顶点变换阶段计算出的信息对每个片断的插值

    这个阶段利用在顶点变换阶段算出的数据,结合连接信息计算出片断的数据。例如,每个顶点包含一个变换后的位置,当它们组成图元时,就可以用来计算图元的片断位置。另一个例子是使用颜色,如果多边形的每个顶点都有自己的颜色值,那么多边形内部片断的颜色值就是各个顶点颜色插值得到的。

    片断纹理化和色彩化(Fragment Texturing and Coloring)

    此阶段的输入是经过插值的片断信息。在前一阶段已经通过插值计算了纹理坐标和一个颜色值,这个颜色在本阶段可以用来和纹理元素进行组合。此外,这一阶段还可以进行雾化处理。通常最后的输出是片断的颜色值以及深度信息。

    光栅操作(Raster Operations)

    此阶段的输入:

    ·像素位置

    ·片断深度和颜色值

    在这个阶段对片断进行一系列的测试,包括:

    ·剪切测试(scissor test)

    ·Alpha测试

    ·模版测试

    ·深度测试

    如果测试成功,则根据当前的混合模式(blend mode)用片断信息来更新像素值。注意混合只能在此阶段进行,因为片断纹理化和颜色化阶段不能访问帧缓存。帧缓存只能在此阶段访问。

    一幅图总结固定功能流水线(Visual Summary of the Fixed Functionality)

    下图直观地总结了上述流水线的各个阶段:


    取代固定的功能(Replacing Fixed Functionality)

    现在的显卡允许程序员自己编程实现上述流水线中的两个阶段:

    ·顶点shader实现顶点变换阶段的功能

    ·片断shader替代片断纹理化和色彩化的功能

     

    顶点处理器

    顶点处理器用来运行顶点shader(着色程序)。顶点shader的输入是顶点数据,即位置、颜色、法线等。

    下面的OpenGL程序发送数据到顶点处理器,每个顶点中包含一个颜色信息和一个位置信息。

    glBegin(...);
        glColor3f(0.2,0.4,0.6);
        glVertex3f(-1.0,1.0,2.0);
        glColor3f(0.2,0.4,0.8);
        glVertex3f(1.0,-1.0,2.0);
    glEnd();
    一个顶点shader可以编写代码实现如下功能:

    ·使用模型视图矩阵以及投影矩阵进行顶点变换

    ·法线变换及归一化

    ·纹理坐标生成和变换

    ·逐顶点或逐像素光照计算

    ·颜色计算

    不一定要完成上面的所有操作,例如你的程序可能不使用光照。但是,一旦你使用了顶点shader,顶点处理器的所有固定功能都将被替换。所以你不能只编写法线变换的shader而指望固定功能帮你完成纹理坐标生成。

    从上一节已经知道,顶点处理器并不知道连接信息,因此这里不能执行拓扑信息有关的操作。比如顶点处理器不能进行背面剔除,它只是操作顶点而不是面。

    顶点shader至少需要一个变量:gl_Position,通常要用模型视图矩阵以及投影矩阵进行变换。顶点处理器可以访问OpenGL状态,所以可以用来处理材质和光照。最新的设备还可以访问纹理。

     

    片断处理器

    片断处理器可以运行片断shader,这个单元可以进行如下操作:

    ·逐像素计算颜色和纹理坐标

    ·应用纹理

    ·雾化计算

    ·如果需要逐像素光照,可以用来计算法线

    片断处理器的输入是顶点坐标、颜色、法线等计算插值得到的结果。在顶点shader中对每个顶点的属性值进行了计算,现在将对图元中的每个片断进行处理,因此需要插值的结果。

    如同顶点处理器一样,当你编写片断shader后,所有固定功能将被取代,所以不能使用片断shader对片断材质化,同时用固定功能进行雾化。程序员必须编写程序实现需要的所有效果。

    片断处理器只对每个片断独立进行操作,并不知道相邻片断的内容。类似顶点shader,我们必须访问OpenGL状态,才可能知道应用程序中设置的雾颜色等内容。

    一个片断shader有两种输出:

    ·抛弃片断内容,什么也不输出

    ·计算片断的最终颜色gl_FragColor,当要渲染到多个目标时计算gl_FragData。

    还可以写入深度信息,但上一阶段已经算过了,所以没有必要。

    需要强调的是片断shader不能访问帧缓存,所以混合(blend)这样的操作只能发生在这之后。


    展开全文
  • GLSL. 语法基础

    千次阅读 2017-04-20 13:26:40
    GLSL. 语法基础
    GLSL语法跟C语言非常相似:
    1.数据类型:
    GLSL包含下面几种简单的数据类型
    float 
    bool :false or ture
    int
    向量:
    vec   {2,3,4}     长度为2, 3, 4的float向量
    bvec {2,3,4}     长度为2, 3, 4的bool向量
    ivec  {2,3,4}     长度为2, 3, 4的int向量
    矩阵:
    mat2   2*2的浮点矩阵
    mat3   3*3的浮点矩阵
    mat4   4*4的浮点矩阵
    以上三种矩阵可以简写为mat2 mat3 mat4
    矩阵的行和列并没有规定相等,因为可以使用mat2*3 mat 4*2等方法来声明行数和列数
    一种特殊的数据类型:取样器--用于纹理采样
    sampler1D     访问一个一维纹理
    sampler2D     访问一个二维纹理
    sampler3D     访问一个三维纹理
    samplerCube  访问一个立方体纹理
    sampler1DShadow 访问一个带对比的一维深度纹理
    sampler2DShadow 访问一个带对比的二维深度纹理

    GLSL提供了类似C语言的用户定义结构:
    struct dirlight{
              vec3 direction;
              vec3 color;
    };

    变量限定符:
    限定符赋给变量特殊的含义:
    const--     用于声明非可写的编译时常量变量
    attribute-- 用于经常更改的信息,只可以再顶点着色器中使用
    uniform--  用于不经常更改的信息,用于顶点着色器和片元着色器
    varying--   用于慈宁宫顶点着色器传递到片元着色器的插值信息

    控制流:
    GLSL的控制流  与C++非常类似,可以使用for while以及do-while实现循环,也可以使用if和if-else进行选择,不过if语句中的变量声明,只是在最近的硬件中才提供

    函数:
    GLSL也提供了一些特殊的实现:
    continue
    break
    discard --只可用于片元着色器,当控制流遇到这个关键字时,正在处理的片元就会被标记为将要丢弃
    函数
    main() 可以返回除了数组外的任何类型
    对于函数的参数 可以使用下面几种限定符
    in -- 复制进函数但不在返回时复制,在函数内部仍然是可写的
    out--只在返回时复制,是可读的
    inout  复制进函数并在返回时复制
    如果没有指定限定符,默认情况下为in
    函数可以通过参数类型重载,但是不能仅仅通过返回类型重载,同样,因为不会执行参数类型自动提升,所以调用函数时参数类型必须完全匹配
    函数不能被递归调用

    GLSL Vertex shader内置的输入变量,注意这些变量都是不可更改的
    attribute vec4  gl_Color;                               顶点数据字段的Diffuse颜色
    attribute vec4  gl_SecondaryColor;                顶点数据字段的Specular颜色
    attribute vec4  gl_Normal;                             顶点法线
    attribute vec4  gl_Vertex;                             顶点位置
    attribute vec4  gl_MultiTexCoord0;                8组贴图坐标
    attribute vec4  gl_MultiTexCoord1;
    attribute vec4  gl_MultiTexCoord2;
    attribute vec4  gl_MultiTexCoord3;
    attribute vec4  gl_MultiTexCoord4;
    attribute vec4  gl_MultiTexCoord5;
    attribute vec4  gl_MultiTexCoord6;
    attribute vec4  gl_MultiTexCoord7;
    attribute vec4  gl_MultiTexCoord0;
    attribute vec4  gl_MultiTexCoord1;
    attribute vec4  gl_FogCoord;                         使用雾效果的参考数值
    在编写shader时,可以把这些输入数据所代表的功能重新定义,名称只是用来让传入数据时有个规则可循而已,C++调用glVertexPointer所指到的vetex buffer数据,在GLSL中可以通过gl_Vertex变量来获得。

    Vertex Shader的输出数据时使用的内置变量:
    vec4 gl_posotion;                用来设置顶点转换到屏幕坐标的位置,Vertex Shader一定要去更新这个数值
    float gl_pointSize;                是启动PointSprite功能时,用来设置矩形大小的数值
    vec4 gl_ClipVertex;              如果启用了Clip Plane功能,gl_ClipVertex可以放入用来与Clip Plane平面做测试用的位置

    下面的输出数据在Vertex Shader中用来输出数据,在Fragment Sahder也可以使用这些变量,但是是用来读取数据:
    araying Vec4   gl_FrontColor;                          对正面做不同的光照计算 ,这两组颜色分主要颜色和次要颜色 代表的是固管的Diffuse值
    varying vec4   gl_BackColor;                            背面
    varying vec4   gl_FrontSecondDaryColor;          固管的Specular值 
    varying vec4   gl_BackSecondaryColor;
    varying vec4   gl_TexCoord[gl_MaxTextureCoords]; glTextCoord[0]是指第0个贴图坐标  
    varying vec4   gl_FogFragCoord;
    Fragment Sahder除了可以从上面几个所列出的变量获得内插结果外,还可以从另外两个内置变量得到一些无法从Vertex Shader获得的数值
    vec4 gl_FragCoord;  gl_FragCoorg.xy代表像素在Framebuffer画面的位置,gl_FragCoord.z代表这个店在做Z Buffer测试时所用的Z值
    bool gl_FrontFacing; 可用来查询目前正在画的像素是来自三角形的正面还是来自他的背面

    Fragment Shader的内置输出变量:
    vec4 gl_FragColor;                               代表画面所要填入的颜色
    vec4 gl_FragData[gl_MaxDrawBuffers];  用来填入画面的颜色,用在启用多个FrameBuffer时,调用gl_FragData填入画面颜色
    vec4 gl_FragDepth;                             用来指定Z Buffer测试时所使用的Z值,这样就可以不通过顶点内插得到的Z值

    对于Vertex Shader来说,除了可通过内置变量来内插数值给Fragment Shader之外,也可以不通过内置变量,只要在Vertex Shader和Fragment Shader中声明相同名称的全局变量,GLSL就可以自动的把这两个数值连接起来

    GLSL的变量命名方式与C语言类似。变量的名称可以使用字母,数字以及下划线,但变量名不能以数字开头,还有变量名不能以gl_作为前缀,这个是GLSL保留的前缀,用于GLSL的内部变量。当然还有一些GLSL保留的名称是不能够作为变量的名称的。

     

    基本类型

    除了布尔型,整型,浮点型基本类型外,GLSL还引入了一些在着色器中经常用到的类型作为基本类型。这些基本类型都可以作为结构体内部的类型。如下表:

     

    类型 描述
    void 跟C语言的void类似,表示空类型。作为函数的返回类型,表示这个函数不返回值。
    bool 布尔类型,可以是true 和false,以及可以产生布尔型的表达式。
    int 整型 代表至少包含16位的有符号的整数。可以是十进制的,十六进制的,八进制的。
    float 浮点型
    bvec2 包含2个布尔成分的向量
    bvec3 包含3个布尔成分的向量
    bvec4 包含4个布尔成分的向量
    ivec2 包含2个整型成分的向量
    ivec3 包含3个整型成分的向量
    ivec4 包含4个整型成分的向量
    mat2 或者 mat2x2 2×2的浮点数矩阵类型
    mat3或者mat3x3 3×3的浮点数矩阵类型
    mat4x4 4×4的浮点矩阵
    mat2x3 2列3行的浮点矩阵(OpenGL的矩阵是列主顺序的)
    mat2x4 2列4行的浮点矩阵
    mat3x2 3列2行的浮点矩阵
    mat3x4 3列4行的浮点矩阵
    mat4x2 4列2行的浮点矩阵
    mat4x3 4列3行的浮点矩阵
    sampler1D 用于内建的纹理函数中引用指定的1D纹理的句柄。只可以作为一致变量或者函数参数使用
    sampler2D 二维纹理句柄
    sampler3D 三维纹理句柄
    samplerCube cube map纹理句柄
    sampler1DShadow 一维深度纹理句柄
    sampler2DShadow 二维深度纹理句柄

    结构体

     

    结构体

    结构体可以组合基本类型和数组来形成用户自定义的类型。在定义一个结构体的同时,你可以定义一个结构体实例。或者后面再定义。

    struct surface {float indexOfRefraction;
    
    vec3 color;float turbulence;
    
    } mySurface;
    
    surface secondeSurface;

    你可以通过=为结构体赋值,或者使用 ==,!=来判断两个结构体是否相等。

    mySurface = secondSurface;

    mySurface == secondSurface;

    只有结构体中的每个成分都相等,那么这两个结构体才是相等的。访问结构体的内部成员使用. 来访问。

    vec3 color = mySurface.color + secondSurface.color;

    结构体至少包含一个成员。固定大小的数组也可以被包含在结构体中。GLSL的结构体不支持嵌套定义。只有预先声明的结构体可以嵌套其中。

    struct myStruct {
    
      vec3 points[3]; //固定大小的数组是合法的
    
      surface surf;  //可以,之前已经定义了
    
      struct velocity {  //不合法float speed;
    
        vec3 direction;
    
      } velo;
    
      subSurface sub; //不合法,没有预先声明;};struct subSurface {  int id;
    };

     

    数组

    GLSL中只可以使用一维的数组。数组的类型可以是一切基本类型或者结构体。下面的几种数组声明是合法的:

    surface mySurfaces[];
    vec4 lightPositions[8];
    vec4 lightPos[] = lightPositions;const int numSurfaces = 5;
    surface myFiveSurfaces[numSurfaces];float[5] values;

    指定显示大小的数组可以作为函数的参数或者使返回值,也可以作为结构体的成员.数组类型内建了一个length()函数,可以返回数组的长度。

    lightPositions.length() //返回数组的大小 8

    最后,你不能定义数组的数组。

     

    修饰符

    变量的声明可以使用如下的修饰符。

    修饰符 描述
    const 常量值必须在声明是初始化。它是只读的不可修改的。
    attribute 表示只读的顶点数据,只用在顶点着色器中。数据来自当前的顶点状态或者顶点数组。它必须是全局范围声明的,不能再函数内部。一个attribute可以是浮点数类型的标量,向量,或者矩阵。不可以是数组或则结构体
    uniform 一致变量。在着色器执行期间一致变量的值是不变的。与const常量不同的是,这个值在编译时期是未知的是由着色器外部初始化的。一致变量在顶点着色器和片段着色器之间是共享的。它也只能在全局范围进行声明。
    varying 顶点着色器的输出。例如颜色或者纹理坐标,(插值后的数据)作为片段着色器的只读输入数据。必须是全局范围声明的全局变量。可以是浮点数类型的标量,向量,矩阵。不能是数组或者结构体。
    centorid varying 在没有多重采样的情况下,与varying是一样的意思。在多重采样时,centorid varying在光栅化的图形内部进行求值而不是在片段中心的固定位置求值。
    invariant (不变量)用于表示顶点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的。所有的数据流和控制流,写入一个invariant变量的是一致的。编译器为了保证结果是完全一致的,需要放弃那些可能会导致不一致值的潜在的优化。除非必要,不要使用这个修饰符。在多通道渲染中避免z-fighting可能会使用到。
    in 用在函数的参数中,表示这个参数是输入的,在函数中改变这个值,并不会影响对调用的函数产生副作用。(相当于C语言的传值),这个是函数参数默认的修饰符
    out 用在函数的参数中,表示该参数是输出参数,值是会改变的。
    inout 用在函数的参数,表示这个参数即是输入参数也是输出参数。

     

    内置变量

    内置变量可以与固定函数功能进行交互。在使用前不需要声明。顶点着色器可用的内置变量如下表:

    名称 类型 描述
    gl_Color vec4 输入属性-表示顶点的主颜色
    gl_SecondaryColor vec4 输入属性-表示顶点的辅助颜色
    gl_Normal vec3 输入属性-表示顶点的法线值
    gl_Vertex vec4 输入属性-表示物体空间的顶点位置
    gl_MultiTexCoordn vec4 输入属性-表示顶点的第n个纹理的坐标
    gl_FogCoord float 输入属性-表示顶点的雾坐标
    gl_Position vec4 输出属性-变换后的顶点的位置,用于后面的固定的裁剪等操作。所有的顶点着色器都必须写这个值。
    gl_ClipVertex vec4 输出坐标,用于用户裁剪平面的裁剪
    gl_PointSize float 点的大小
    gl_FrontColor vec4 正面的主颜色的varying输出
    gl_BackColor vec4 背面主颜色的varying输出
    gl_FrontSecondaryColor vec4 正面的辅助颜色的varying输出
    gl_BackSecondaryColor vec4 背面的辅助颜色的varying输出
    gl_TexCoord[] vec4 纹理坐标的数组varying输出
    gl_FogFragCoord float 雾坐标的varying输出

    片段着色器的内置变量如下表:

    名称 类型 描述
    gl_Color vec4 包含主颜色的插值只读输入
    gl_SecondaryColor vec4 包含辅助颜色的插值只读输入
    gl_TexCoord[] vec4 包含纹理坐标数组的插值只读输入
    gl_FogFragCoord float 包含雾坐标的插值只读输入
    gl_FragCoord vec4 只读输入,窗口的x,y,z和1/w
    gl_FrontFacing bool 只读输入,如果是窗口正面图元的一部分,则这个值为true
    gl_PointCoord vec2 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。
    gl_FragData[] vec4 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
    gl_FragColor vec4 输出的颜色用于随后的像素操作
    gl_FragDepth float 输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替

     

    表达式

     

    操作符

    GLSL语言的操作符与C语言相似。如下表(操作符的优先级从高到低排列)

    操作符 描述
    () 用于表达式组合,函数调用,构造
    [] 数组下标,向量或矩阵的选择器
    . 结构体和向量的成员选择
    ++ – 前缀或后缀的自增自减操作符
    + – ! 一元操作符,表示正 负 逻辑非
    * / 乘 除操作符
    + - 二元操作符 表示加 减操作
    <> <= >= == != 小于,大于,小于等于, 大于等于,等于,不等于 判断符
    && || ^^ 逻辑与 ,或,  异或
    ?: 条件判断符
    = += –= *=  /= 赋值操作符
    , 表示序列

    像 求地址的& 和 解引用的 * 操作符不再GLSL中出现,因为GLSL不能直接操作地址。类型转换操作也是不允许的。 位操作符(&,|,^,~, <<, >> ,&=, |=, ^=, <<=, >>=)是GLSL保留的操作符,将来可能会被使用。还有求模操作(%,%=)也是保留的。

     

    数组访问

    数组的下标从0开始。合理的范围是[0, size - 1]。跟C语言一样。如果数组访问越界了,那行为是未定义的。如果着色器的编译器在编译时知道数组访问越界了,就会提示编译失败。

    vec4 myColor, ambient, diffuse[6], specular[6];

    myColor = ambient + diffuse[4] + specular[4];

     

    构造函数

    构造函数可以用于初始化包含多个成员的变量,包括数组和结构体。构造函数也可以用在表达式中。调用方式如下:

    vec3 myNormal = vec3(1.0, 1.0, 1.0);

    greenTint = myColor + vec3(0.0, 1.0, 0.0);

    ivec4 myColor = ivec4(255);

    还可以使用混合标量和向量的方式来构造,只要你的元素足以填满该向量。

    vec4 color = vec4(1.0, vec2(0.0, 1.0), 1.0);

    vec3 v = vec3(1.0, 10.0, 1.0);

    vec3 v1 = vec3(v);

    vec2 fv = vec2(5.0, 6.0);

    float f = float(fv); //用x值2.5构造,y值被舍弃

    对于矩阵,OpenGL中矩阵是列主顺序的。如果只传了一个值,则会构造成对角矩阵,其余的元素为0.

    mat3 m3 = mat3(1.0);

    构造出来的矩阵式:

    1.0 0.0 0.0

    0.0 1.0 0.0

    0.0 0.0 1.0

    mat2 matrix1 = mat2(1.0, 0.0, 0.0, 1.0);

    mat2 matrix2 = mat2(vec2(1.0, 0.0), vec2(0.0, 1.0));

    mat2 matrix3 = mat2(1.0); 

    mat2 matrix4 = mat2(mat4(2.0)); //会取 4×4矩阵左上角的2×2矩阵。

    构造函数可以用于标量数据类型的转换。GLSL不支持隐式或显示的转换,只能通过构造函数来转。其中int转为float值是一样的。float转为int则小数部分被丢弃。int或float转为bool,0和0.0转为false,其余的值转为true. bool转为int或float,false值转为0和0.0,true转为1和1.0.

    float f = 1.7;

    int I = int(f); // I = 1

    数组的初始化,可以在构造函数中传入值来初始化数组中对应的每一个值。

    ivec2 position[3] = ivec2[3]((0,0), (1,1), (2,2));

    ivec2 pos2[3] = ivec2[]((3,3), (2,1), (3,1));

    构造函数也可以对结构体进行初始化。其中顺序和类型要一一对应。

    struct surface {  int  index;
      vec3 color;  float rotate;
    };
    
    surface mySurface = surface(3, vec3(red, green, blue), 0.5);

     

    成分选择

    向量中单独的成分可以通过{x,y,z,w},{r,g,b,a}或者{s,t,p,q}的记法来表示。这些不同的记法用于顶点,颜色,纹理坐标。在成分选择中,你不可以混合使用这些记法。其中{s,t,p,q}中的p替换了纹理的r坐标,因为与颜色r重复了。下面是用法举例:

    vec3 myVec = {0.5, 0.35, 0.7};float r = myVec.r;float myYz = myVec.yz;float myQ = myVec.q;//出错,数组越界访问,q代表第四个元素float myRY = myVec.ry; //不合法,混合使用记法

    较特殊的使用方式,你可以重复向量中的元素,或者颠倒其顺序。如:

    vec3 yxz = myVec.yxz; //调换顺序vec4 mySSTT = myVec.sstt; //重复其中的值

    在赋值是,也可以选择你想要的顺序,但是不能重复其中的成分。

    vec4 myColor = {0.0, 1.0, 2.0, 1.0};
    myColor.x = -1.0;
    myColor.yz = vec2(3.0, 5.0);
    myColor.wx = vec2(1.0, 3.0);
    myColor.zz = vec2(2.0, 3.0); //不合法

    我们也可以通过使用下标来访问向量或矩阵中的元素。如果越界那行为将是未定义的。

    float myY = myVec[1];

    在矩阵中,可以通过一维的下标来获得该列的向量(OpenGL的矩阵是列主顺序的)。二维的小标来获得向量中的元素。

    mat3 myMat = mat3(1.0);
    vec3 myVec = myMat[0]; //获得第一列向量 1.0, 0.0, 0.0float f = myMat[0][0]; // 第一列的第一个向量。

     

    控制流

     

    循环

    与C和C++相似,GLSL语言也提供了for, while, do/while的循环方式。使用continue跳入下一次循环,break结束循环。

    for (l = 0; l < numLights; l++)
    {if (!lightExists[l])continue;
        color += light[l];
    }while (i < num)
    {
        sum += color[i];
        i++;
    }do{
        color += light[lightNum];
        lightNum--;
    }while (lightNum > 0)

     

    if/else

    color = unlitColor;if (numLights > 0)
    {
        color = litColor;
    }else{
        color = unlitColor;
    }

     

    discard

    片段着色器中有一种特殊的控制流成为discard。使用discard会退出片段着色器,不执行后面的片段着色操作。片段也不会写入帧缓冲区。

    if (color.a < 0.9)

    discard;

     

    函数

    在每个shader中必须有一个main函数。main函数中的void参数是可选的,但返回值是void时必须的。

    void main(void)
    {
     ...
    }

    GLSL中的函数,必须是在全局范围定义和声明的。不能在函数定义中声明或定义函数。函数必须有返回类型,参数是可选的。参数的修饰符(in, out, inout, const等)是可选的。

    //函数声明bool isAnyNegative(const vec4 v);//函数调用void main(void)
    {bool isNegative = isAnyNegative(gl_Color);
        ...
    }//定义bool isAnyNegative(const vec4 v)
    {if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0)return true;elsereturn false;
    }

    结构体和数组也可以作为函数的参数。如果是数组作为函数的参数,则必须制定其大小。在调用传参时,只传数组名就可以了。

    vec4 sumVectors(int sumSize, vec4 v[10]);void main()
    {
        vec4 myColors[10];
        ...
        vec4 sumColor = sumVectors(5, myColors);
    }
    
    vec4 sumVectors(int sumSize, vec4 v[10])
    {int i = 0;
        vec4 sum = vec4(0.0);for(; i < sumSize; ++i)
        {
            sum += v[i]; 
        }return sum;
    }

    GLSL的函数是支持重载的。函数可以同名但其参数类型或者参数个数不同即可。

    float sum(float a, float b)
    {return a + b;
    }
    
    vec3 sum(vec3 v1, vec3 v2)
    {return v1 + v2;
    }

    GLSL中函数递归是不被允许的。其行为是未定义的。


    展开全文
  • GLSL实现图像处理

    万次阅读 2015-05-06 21:02:23
     这些操作其本质上是属于图像处理的东西,OpenGL实现图像处理主要是用到了GLSL着色语言。具体到着色器就是片段着色器。 说白了就是如何用OpenGL做通用GPU计算,OpenGL做通用计算的步骤主要如下:读取数据->顶点

    今天晚上没事将以前弄的OPENGL着色语言实现滤镜效果的实现和大家分享一下,滤镜主要包括图像的对比度调整、浮雕效果、彩色图片灰度化、卷积等操作。

             这些操作其本质上是属于图像处理的东西,OpenGL实现图像处理主要是用到了GLSL着色语言。具体到着色器就是片段着色器。

             说白了就是如何用OpenGL做通用GPU计算,OpenGL做通用计算的步骤主要如下:

    读取数据->顶点着色器->片段着色器->渲染到纹理->从纹理读写数据。

     

             步骤具体细化如下:

    (1)初始化。

             初始化窗口系统。

    void InitGLUT(int argc,char** argv)

    {

        glutInit(&argc,argv);

        glutWindow = glutCreateWindow("GPGPU");

    }

     

    初始化FBO。在此阶段还需要将投影设置为正摄投影的方式,视口与图像的幅宽一样大,并且还要讲纹理重采样方式设置为最邻近采样,这样就保证了片段操作的时候取得每个数据都是原始数据。

    void InitFBO(int nWidth,int nHeight,GLuint*pFb)

    {

        //创建FBO并绑定

        glewInit();

        GLuint fb;

        glGenFramebuffersEXT(1,&fb);

        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,fb);

        *pFb = fb;

     

        //用绘制来调用

        glMatrixMode(GL_PROJECTION);

        glLoadIdentity();

        gluOrtho2D(0,nWidth,0,nHeight);

        glMatrixMode(GL_MODELVIEW);

        glLoadIdentity();

        glViewport(0,0,nWidth,nHeight);

    }

     

    初始化shader,包括从文件中读取数据以及编译等操作。

    void InitGLSL()

    {

        //创建顶点shader

        vertShader = glCreateShader(GL_VERTEX_SHADER);

        if( 0 == vertShader)

        {

            fprintf(stderr,"Error creating vertex shader.\n");

            exit(1);

        }

     

        GLint result;

     

        //加载片元着色器

        fragShader = glCreateShader(GL_FRAGMENT_SHADER);

        if( 0 == fragShader)

        {

            fprintf(stderr,"片元着色器创建失败.\n");

            exit(1);

        }

     

        //c拷贝shader源码

        const GLchar *shaderCode2 = loadShaderAsString("BrightNess.glsl");

        glShaderSource( fragShader,1, &shaderCode2, NULL );

     

        delete []shaderCode2;

     

        //编译shader

        glCompileShader(fragShader);

     

        //检查编译状态

        glGetShaderiv( fragShader,GL_COMPILE_STATUS, &result );

        if (GL_FALSE == result)

        {

            fprintf( stderr,"片元着色器编译失败!\n" );

            GLint logLen;

            glGetShaderiv( fragShader,GL_INFO_LOG_LENGTH, &logLen );

            if( logLen > 0)

            {

                char * log = (char *)malloc(logLen);

                GLsizei written;

                glGetShaderInfoLog(fragShader,logLen, &written,log);

                fprintf(stderr, "Shaderlog:\n%s", log);

                free(log);

            }

        }

     

        //创建程序对象

        programHandle = glCreateProgram();

        if( 0 == programHandle)

        {

            fprintf(stderr,"Error creating program object.\n");

            exit(1);

        }

     

        //将着色器链接到程序对象

        glAttachShader(programHandle,fragShader);

     

        //链接程序

        glLinkProgram(programHandle);

     

        //检查链接状态

        GLint status;

        glGetProgramiv( programHandle,GL_LINK_STATUS, &status );

        if( GL_FALSE == status )

        {

            fprintf( stderr,"链接失败!\n" );

            GLint logLen;

            glGetProgramiv(programHandle,GL_INFO_LOG_LENGTH,

                &logLen);

            if( logLen > 0)

            {

                char * log = (char *)malloc(logLen);

                GLsizei written;

                glGetProgramInfoLog(programHandle,logLen,

                    &written, log);

                fprintf(stderr,"Program log: \n%s", log);

                free(log);

            }

     

            glDeleteProgram(programHandle);

        }

     

        else

        {

            glUseProgram(programHandle);

        }

    }

     

    (2)创建纹理对象以及设置参数

    这里需要创建两个纹理对象,一个用于输入图像,另外一个用于保存输出的结果,即将保存结果的纹理对象绑定到帧缓存对象上,即实现渲染到纹理的目标。

    //创建纹理对象并绑定

        GLuint xTexID,yTexID;

        glGenTextures(1,&xTexID);

        glGenTextures(1,&yTexID);

     

    设置纹理属性如下:

    void SetupTexture(stTextureParatextureParameters,constGLuint texID,int nWidth,int nHeight)

    {

        glBindTexture(textureParameters.texTarget,texID);

     

        //设置纹理参数

        glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

        glTexParameteri(textureParameters.texTarget,GL_TEXTURE_MAG_FILTER,GL_NEAREST);

        glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_S,GL_CLAMP);

        glTexParameteri(textureParameters.texTarget,GL_TEXTURE_WRAP_T,GL_CLAMP);

        //定义纹理数据单元类型

        glTexImage2D(textureParameters.texTarget,0,textureParameters.texInternalFormat,nWidth,nHeight,0,

            textureParameters.texFormat,GL_FLOAT,0);

     

    }

     

    然后将输入数据加载到输入纹理缓存上面

    //传入输入数据

        glBindTexture(textureParameters.texTarget,xTexID);

    glTexSubImage2D(textureParameters.texTarget,0,0,0,nWidth,nHeight,textureParameters.texFormat,GL_FLOAT,pfInput);

    //设置映射参数,替换原来的数据

        glTexEnvi(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE);

     

    最后是将输出纹理绑定到帧缓存对象上并激活输入纹理对象。

    //绑定输出纹理缓存

    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,textureParameters.texTarget,yTexID,0);

     

        //激活当前输入的纹理单元

        glActiveTexture(GL_TEXTURE0);

     

    绑定的关联点就是GL_COLOR_ATTACHMENT0_EXT,它代表是帧缓存中的一个颜色缓存。

     

    (3)设置渲染的对象

    glDrawBuffer(GL_COLOR_ATTACHMENT0_EXT);

    glPolygonMode(GL_FRONT,GL_FILL);

     

    (4)通过绘制来调用“核函数”

    这里是一个形象的比喻,就是通过绘制操作来实现对图像的处理,类似于CUDA和opencl里面的核函数调用。

    //用绘制进行调用

        glBegin(GL_QUADS);

        glTexCoord2f(0,0);

        glVertex2f(0,0);

        glTexCoord2f(nWidth,0);

        glVertex2f(nWidth,0);

        glTexCoord2f(nWidth,nHeight);

        glVertex2f(nWidth,nHeight);

        glTexCoord2f(0,nHeight);

        glVertex2f(0,nHeight);

        glEnd();

     

        glFinish();

     

    (5)读取结果数据以及进一步处理

    //从当前纹理缓存拷贝数据

    void TransformFromTex(intnWidth,int nHeight,float *pfData)

    {

        //读取数据

        glReadBuffer(GL_COLOR_ATTACHMENT0_EXT);

        glReadPixels(0,0,nWidth,nHeight,GL_RGB,GL_FLOAT,pfData);

    }

     

    这相当于将数据从显存拷贝到主机内存,然后可以写入文件等。

     

    说了这么多,最后还剩下一个片段着色器的编写。上面相当于把框架已经搭建好,然后只需要更改片段着色器实现不同的图像处理效果了。

    这边就以一副风景画为例子实现集中不同的效果。

    原图如下:


    1、对比度调整,代码如下:

    #version 400 compatibility
    
    #extension GL_ARB_texture_rectangle : enable
    uniform sampler2DRect uImageUint;
    
    float uT = 0.5;
    
    out vec4 outColor;
    //图像对比度增强
    
    void main()
    {
        vec2 pos = gl_TexCoord[0].st;
        vec3 irgb = texture(uImageUint,pos).rgb;
        //vec3 target = vec3(128.0,128.0,128.0);
        
        vec3 target = vec3(0.0,0.0,0.0);
        
        outColor = vec4(mix(target,irgb,uT),1.0);
    }

    处理后效果如下:


    2、浮雕效果代码如下:

    #version 400 compatibility
    
    #extension GL_ARB_texture_rectangle : enable
    uniform sampler2DRect tex;
    
    out vec4 outColor;
    //浮雕效果
    
    void main()
    {
        vec2 pos = gl_TexCoord[0].st;
        //获得纹理的尺寸
        ivec2 vecSize = textureSize(tex);
        float ResS = float(vecSize.s);
        float ResT = float(vecSize.t);
        vec3 irgb = texture(tex,pos).rgb;
        
        vec2 stpp = vec2(-1.0,-1.0);
        vec3 c00 = texture(tex,pos).rgb;
        vec3 cp1p1 = texture(tex,pos+stpp).rgb;
        
        vec3 diffs = c00 - cp1p1;
        float maxV = diffs.r;
        if(abs(diffs.g) > abs(maxV)) maxV = diffs.g;
        if(abs(diffs.b) > abs(maxV)) maxV = diffs.b;
        
        float gray = clamp(maxV + 128,0.0,255.0);
        outColor = vec4(gray,gray,gray,1.0);
    }


    处理后的图像如下:



    3、颠倒显示

    #version 400 compatibility
    
    #extension GL_ARB_texture_rectangle : enable
    uniform sampler2DRect tex;
    
    out vec4 outColor;
    //图像翻转效果
    
    void main()
    {
        vec2 pos = gl_TexCoord[0].st;
        //获得纹理的尺寸
    	ivec2 vecSize = textureSize(tex);
        int nWidth = (vecSize.s);
        int nHeight = (vecSize.t);
        pos = vec2(nWidth- pos.x-1,nHeight - pos.y - 1);
        vec3 irgb = texture(tex,pos).rgb;
        
        outColor = vec4(irgb,1.0);
    }

    处理后效果如下:



    4、图像黑白化

    #version 400 compatibility
    
    #extension GL_ARB_texture_rectangle : enable
    uniform sampler2DRect tex;
    
    const vec3 W = vec3(0.2125,0.7154,0.0721);
    
    out vec4 outColor;
    void main()
    {
        vec2 pos = gl_TexCoord[0].st;
        
    	vec3 color = texelFetch(tex,ivec2(pos.x+0.5,pos.y+0.5)).rgb;
        float luminace = dot(color,W);
        outColor = vec4(luminace,luminace,luminace,1.0);
        //gl_FragColor = fResult;
    }
    处理后图片如下:


    5、图像的卷积,这个稍微复杂一点,其实也很简单,基本图像处理的东西。

    #version 400 compatibility
    
    #extension GL_ARB_texture_rectangle : enable
    uniform sampler2DRect tex;
    float fRadius = 2;
    
    out vec4 outColor;
    
    void main()
    {
    	vec2 pos = gl_TexCoord[0].st;
    	vec4 fSum = vec4(0.0,0.0,0.0,0.0);
    	vec4 fTotal = vec4(0.0,0.0,0.0,0.0);
    	vec4 fResult = vec4(0.0,0.0,0.0,0.0);
    	
    	ivec2 vecSize = textureSize(tex);
    	
    	for (float i = pos.x - fRadius; i < pos.x + fRadius + 0.5; i += 1.0)
    		for (float j = pos.y - fRadius; j < pos.y + fRadius + 0.5; j += 1.0)
    		{
    			if (i >=0 && j >= 0 && i < vecSize.x && j < vecSize.y)
    			{
    				fSum += texture2DRect(tex,vec2(i+0.5,j+0.5));
    				fTotal += vec4(1.0,1.0,1.0,1.0);
    			}
    		}
    	vec4 color = texelFetch(tex,ivec2(pos.x+0.5,pos.y+0.5));
    	fResult = fSum/fTotal;
    	outColor = fResult;
    	//gl_FragColor = fResult;
    }


    处理后的效果如下:


    上面这些都是最基本的图像处理操作,使用shader其实就是利用了显卡的并行数据处理能力,在今天的大数据时代以及资源不足的手机上,如果能充分利用GPU的优势,对于图像的处理将会有速度上的很大提升。




    展开全文
  • GLSL入门

    千次阅读 2018-07-01 02:18:46
    着色器语言 GLSL (opengl-shader-language)入门大全基本类型:类型说明void空类型,即不返回任何值bool布尔类型 true,falseint带符号的整数 signed integerfloat带符号的浮点数 floating scalarvec2, vec3, vec4n维...
  • GLSL版本的区别和对比

    2018-11-18 20:29:00
    之前尝试将一个GLSL version 110的版本写成GLSL version 330的,在此将学习过程和收获记录下来。 参考链接 GLSL Versions   介绍 你可以使用#version命令作为着色器的第一行来指定GLSL版本: #version 120 ...
  • 高级GLSL

    千次阅读 2018-08-20 20:16:39
    1. 高级GLSL 1.1 GLSL的内建变量 1.1.1 顶点着色器 内建变量 1.1.1.1 gl_PointSize 1.1.1.2 gl_VertexID 1.1.2 片段着色器 内建变量 1.1.2.1 gl_FragCoord 1.1.2.2 gl_FrontFacing 1.1.2.3 gl_FragDepth 1.2 ...
  • 高级GLSL简介

    2019-10-23 09:53:32
    GLSL(GL Shading Language)的着色器代码分成2个部分:Vertex Shader(顶点着色器)和Fragment(片断着色器),有时还会有Geometry Shader(几何着色器)。负责运行顶点着色的是顶点着色器。它可以得到当前OpenGL ...
  • GLSL(着色器语言)

    千次阅读 2017-10-11 10:01:07
    GLSL(着色器语言) 简介OpenGLES的着色器语言GLSL是一种高级的图形化编程语言,其源自应用广泛的C语言。与传统的c语言不同的是,它提供了更加丰富的针对于图像处理的原生类型,诸如向量、矩阵之类。OpenGLES 主要...
  • GLSL 中文手册

    万次阅读 2018-09-10 16:51:08
    转载自 GLSL 中文手册 基本类型: 类型 说明 void 空类型,即不返回任何值 bool 布尔类型 true,false int 带符号的整数 signed integer float 带符号的浮点数 floating scalar vec2, vec3...
  • GLSL 内建函数

    万次阅读 多人点赞 2012-08-17 10:50:54
    OpenGL ES着色语言为标量和向量操作定义了一套内建便利函数。有些内建函数可以用在多个类型的着色器中,有些是针对固定硬件的,所以这部分只能用在某个特定的着色器上。  内建函数基本上可以分为一下三类: ...
  • GLSL基础概念和用法

    万次阅读 2016-11-15 09:47:20
    图形图像顶点和片段像素的渲染,只能用固定管线或可编程着色器任何一种进行处理。 一、顶点着色器 顶点着色器,根据应用程序的设计,只是选择处理: 1.视觉空间变换(模型,法线,纹理). 2.主颜色和辅助颜色的计算生成...
  • 孤立森林

    千次阅读 热门讨论 2018-08-11 15:05:40
    1、孤立森林介绍 iForest(IsolationForest)孤立森林是一个基于Ensemble的快速异常检测方法,具有线性时间复杂度和高精准度,是符合大数据处理要求的state-of-the-art算法。 其可以用于网络安全中的攻击检测,...
  • 孤立森林异常检测

    2019-04-07 09:54:31
    孤立森林异常检测,Outlier Detection with Isolation Forest 。
  • 孤立森林python实现

    千次阅读 2019-06-13 16:29:12
    孤立森林是近几年较为火热的检测异常数据算法,下面使用python来编写代码。 本人初学python,若有错误之处,欢迎指出(csdn再导入python代码时格式有些乱) “”" 1.创建一个导入数据,选取子样本类 2.创建一个随机...
  • 孤立森林(Isolation Forest)算法剖析

    千次阅读 2019-07-25 23:28:52
    孤立森林(Isolation Forest)算法是西瓜书作者周志华老师的团队研究开发的算法,一般用于结构化数据的异常检测。 异常的定义 针对于不同类型的异常,要用不同的算法来进行检测,而孤立森林算法主要针对的是连续型...
  • 孤立森林(Isolation Forest)

    万次阅读 多人点赞 2018-05-01 22:26:04
    背景 现有的异常检测方法主要是通过对正常样本的描述,给出一个正常样本在特征空间中的区域,对于不在这个区域中的样本,视为异常。...孤立森林,不再是描述正常的样本点,而是要孤立异常点,由周志华教授...
  • 我的机器学习教程「美团」算法工程师带你入门机器学习 已经开始更新了,欢迎大家订阅~ 任何关于算法、编程、AI行业知识或博客内容的问题,可以随时扫码关注公众号「图灵的猫」,加入”学习小组“,沙雕博主在线答疑...
  • 孤立森林(Iforest) 异常检测方法

    千次阅读 2019-08-02 11:25:25
    孤立森林(Iforest) 异常检测方法 概述 Iforest算法常用于异常检测。孤立森林算法由08年首次提出,基于孤立森林的异常检测算法11年在tkdd问世,这两篇论文的一作是小哥Fei Tony Liu和Kai Ming Ting教授,周志华是第...
  • 孤立森林算法思想及代码实现

    千次阅读 2020-02-17 14:56:37
    孤立森林是属于无监督学习范畴中检测异常值的一种模型,他不同于其他通过计算距离和密度来识别样本点是否是孤立点,而是通过样本的疏密程度来判断样本的是否孤立。仅适用于连续数据。 孤立森林采用多重二分法将...
  • 树模型-孤立森林

    千次阅读 2018-09-14 20:42:05
    树模型 ...​ 孤立森林(Isolation Foreset)是基于树(iTree)集成的快速异常检测方法,其异常检测的核心思想是“异常点是容易被孤立的离群点”。因此,孤立森林采用随机特征随机阈值划分生...
  • 基于孤立森林的异常检测

    千次阅读 2019-02-17 20:28:04
    iForest (Isolation Forest)孤立森林 是一个基于Ensemble的快速异常检测方法,具有线性时间复杂度和高精准度,是符合大数据处理要求的state-of-the-art算法,由周志华教授等人提出,在工业界得到很好的应用。...
  • 寻找异常点
  • 背景 现有的异常检测方法:通过对正常样本的描述,给出一个正常样本在特征空间中的区域,对于不在这个区域中的样本,视为异常。这些方法的主要缺点是,异常检测器只会对正常样本的描述做优化,而不会对异常样本的...
  • iForest (Isolation Forest)孤立森林 是一个基于Ensemble的快速异常检测方法,具有线性时间复杂度和高精准度,是符合大数据处理要求的state-of-the-art算法(详见新版教材“Outlier Analysis”第5和第6章 PDF)。...
  • 孤立森林(Isolation Forest)简称iForest,此算法对内存要求很低,且处理速度很快,其时间复杂度也是线性的。可以很好的处理高维数据和大数据,并且也可以作为在线异常检测。 算法简介      ...
  • 孤立森林或“iForest”是一个非常漂亮和优雅简单的算法,可以用很少的参数来识别异常。原始的论文对广大的读者来说是容易理解的,并且包含了很少的数学知识。在这篇文章中,我将解释为什么iForest是目前最好的大数据...
  • 孤立森林算法sklearn实现,源码分析 算法一: 首先初始化一些参数 class sklearn.ensemble.IsolationForest(n_estimators=100, max_samples=’auto’, contamination=’legacy’, max_features=1.0, ...
  • GLSL

    千次阅读 2013-11-08 10:38:53
    OpenGL Shading Language (GLSL)
  • 算法1:iForest(X,t,ψ)iForest(X,t,\psi)iForest(X,t,ψ)构建孤立森林,X表示我们的总数据, t 表示孤立森林中设置的树的棵数,ψ\psiψ表示构建一棵树,我们从总数据X中选取的随机样本个数 算法1:iTree(X′)...

空空如也

1 2 3 4 5 ... 20
收藏数 15,961
精华内容 6,384
关键字:

glsl