glsl_孤立森林 - CSDN
精华内容
参与话题
  • OpenGL进阶(八) - GLSL入门

    千次阅读 2013-08-15 14:29:16
    简介  随着图形硬件的发展,渲染管线由固定... 今天要介绍的就是和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 中文手册

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

    转载自 GLSL 中文手册

    基本类型:

    类型 说明
    void 空类型,即不返回任何值
    bool 布尔类型 true,false
    int 带符号的整数 signed integer
    float 带符号的浮点数 floating scalar
    vec2, vec3, vec4 n维浮点数向量 n-component floating point vector
    bvec2, bvec3, bvec4 n维布尔向量 Boolean vector
    ivec2, ivec3, ivec4 n维整数向量 signed integer vector
    mat2, mat3, mat4 2x2, 3x3, 4x4 浮点数矩阵 float matrix
    sampler2D 2D纹理 a 2D texture
    samplerCube 盒纹理 cube mapped texture

    基本结构和数组:

    类型 说明
    结构 struct type-name{} 类似c语言中的 结构体
    数组 float foo[3] glsl只支持1维数组,数组可以是结构体的成员

    向量的分量访问:

    glsl中的向量(vec2,vec3,vec4)往往有特殊的含义,比如可能代表了一个空间坐标(x,y,z,w),或者代表了一个颜色(r,g,b,a),再或者代表一个纹理坐标(s,t,p,q)
    所以glsl提供了一些更人性化的分量访问方式.

    vector.xyzw 其中xyzw 可以任意组合

    vector.rgba 其中rgba 可以任意组合

    vector.stpq 其中rgba 可以任意组合

    vec4 v=vec4(1.0,2.0,3.0,1.0);
    float x = v.x; //1.0
    float x1 = v.r; //1.0
    float x2 = v[0]; //1.0
    
    vec3 xyz = v.xyz; //vec3(1.0,2.0,3.0)
    vec3 xyz1 = vec(v[0],v[1],v[2]); //vec3(1.0,2.0,3.0)
    vec3 rgb = v.rgb; //vec3(1.0,2.0,3.0)
    
    vec2 xyzw = v.xyzw; //vec4(1.0,2.0,3.0,1.0);
    vec2 rgba = v.rgba; //vec4(1.0,2.0,3.0,1.0);
    

    运算符:

    优先级(越小越高) 运算符 说明 结合性
    1 () 聚组:a*(b+c) N/A
    2 [] () . ++ – 数组下标[],方法参数fun(arg1,arg2,arg3),属性访问a.b,自增/减后缀a++ a– L - R
    3 ++ – + - ! 自增/减前缀++a –a,正负号(一般正号不写)a ,-a,取反!false R - L
    4 * / 乘除数学运算 L - R
    5 + - 加减数学运算 L - R
    7 < > <= >= 关系运算符 L - R
    8 == != 相等性运算符 L - R
    12 && 逻辑与 L - R
    13 ^^ 逻辑排他或(用处基本等于!=) L - R
    14 || 逻辑或 L - R
    15 ? : 三目运算符 L - R
    16 = += -= *= /= 赋值与复合赋值 L - R
    17 , 顺序分配运算 L - R

    ps 左值与右值:

    左值:表示一个储存位置,可以是变量,也可以是表达式,但表达式最后的结果必须是一个储存位置.
    
    右值:表示一个值, 可以是一个变量或者表达式再或者纯粹的值.
    
    操作符的优先级:决定含有多个操作符的表达式的求值顺序,每个操作的优先级不同.
    
    操作符的结合性:决定相同优先级的操作符是从左到右计算,还是从右到左计算。
    

    基础类型间的运算:

    glsl中,没有隐式类型转换,原则上glsl要求任何表达式左右两侧(l-value),(r-value)的类型必须一致 也就是说以下表达式都是错误的:

    int a =2.0; //错误,r-value为float 而 lvalue 为int.
    int a =1.0+2;
    float a =2;
    float a =2.0+1;
    bool a = 0; 
    vec3 a = vec3(1.0, 2.0, 3.0) * 2;
    

    下面来分别说说可能遇到的情况:

    1.floatint:

    float与float , int与int之间是可以直接运算的,但float与int不行.它们需要进行一次显示转换.即要么把float转成int: int(1.0)
    ,要么把int转成float: float(1) ,以下表达式都是正确的:

    int a=int(2.0);
    float a= float(2);
    
    int a=int(2.0)*2 + 1;
    float a= float(2)*6.0+2.3;

    2.floatvec(向量) mat(矩阵):

    vec,mat这些类型其实是由float复合而成的,当它们与float运算时,其实就是在每一个分量上分别与float进行运算,这就是所谓的逐分量运算.glsl里
    大部分涉及vec,mat的运算都是逐分量运算,但也并不全是. 下文中就会讲到特例.

    逐分量运算是线性的,这就是说 vec 与 float 的运算结果是还是 vec.

    int 与 vec,mat之间是不可运算的, 因为vec和mat中的每一个分量都是 float 类型的. 无法与int进行逐分量计算.

    下面枚举了几种 float 与 vec,mat 运算的情况

    vec3 a = vec3(1.0, 2.0, 3.0);
    mat3 m = mat3(1.0);
    float s = 10.0;
    vec3 b = s * a; // vec3(10.0, 20.0, 30.0)
    vec3 c = a * s; // vec3(10.0, 20.0, 30.0)
    mat3 m2 = s * m; // = mat3(10.0)
    mat3 m3 = m * s; // = mat3(10.0)
    

    3. vec(向量)vec(向量):

    两向量间的运算首先要保证操作数的阶数都相同.否则不能计算.例如: vec3*vec2 vec4+vec3 等等都是不行的.

    它们的计算方式是两操作数在同位置上的分量分别进行运算,其本质还是逐分量进行的,这和上面所说的float类型的
    逐分量运算可能有一点点差异,相同的是 vec 与 vec 运算结果还是 vec, 且阶数不变.

    vec3 a = vec3(1.0, 2.0, 3.0);
    vec3 b = vec3(0.1, 0.2, 0.3);
    vec3 c = a + b; // = vec3(1.1, 2.2, 3.3)
    vec3 d = a * b; // = vec3(0.1, 0.4, 0.9)

    3. vec(向量)mat(矩阵):

    要保证操作数的阶数相同,且vec与mat间只存在乘法运算.

    它们的计算方式和线性代数中的矩阵乘法相同,不是逐分量运算.

    vec2 v = vec2(10., 20.);
    mat2 m = mat2(1., 2.,  3., 4.);
    vec2 w = m * v; // = vec2(1. * 10. + 3. * 20., 2. * 10. + 4. * 20.)
    ...
    
    vec2 v = vec2(10., 20.);
    mat2 m = mat2(1., 2.,  3., 4.);
    vec2 w = v * m; // = vec2(1. * 10. + 2. * 20., 3. * 10. + 4. * 20.)
    
    

    向量与矩阵的乘法规则如下:

    4. mat(矩阵)mat(矩阵):

    要保证操作数的阶数相同.

    在mat与mat的运算中, 除了乘法是线性代数中的矩阵乘法外.其余的运算任为逐分量运算.简单说就是只有乘法是特殊的,其余都和vec与vec运算类似.

    mat2 a = mat2(1., 2.,  3., 4.);
    mat2 b = mat2(10., 20.,  30., 40.);
    mat2 c = a * b; //mat2(1.*10.+3.*20.,2.*10.+4.*20.,1.* 30.+3.*40.,2.* 30.+4.*40.);
    
    mat2 d = a+b;//mat2(1.+10.,2.+20.,3.+30.,4.+40);
    

    矩阵乘法规则如下:

    变量限定符:

    修饰符 说明
    none (默认的可省略)本地变量,可读可写,函数的输入参数既是这种类型
    const 声明变量或函数的参数为只读类型
    attribute 只能存在于vertex shader中,一般用于保存顶点或法线数据,它可以在数据缓冲区中读取数据
    uniform 在运行时shader无法改变uniform变量, 一般用来放置程序传递给shader的变换矩阵,材质,光照参数等等.
    varying 主要负责在vertex 和 fragment 之间传递变量

    const:

    和C语言类似,被const限定符修饰的变量初始化后不可变,除了局部变量,函数参数也可以使用const修饰符.但要注意的是结构变量可以用const修饰,
    但结构中的字段不行.

    const变量必须在声明时就初始化 const vec3 v3 = vec3(0.,0.,0.)

    局部变量只能使用const限定符.

    函数参数只能使用const限定符.

    struct light {
            vec4 color;
            vec3 pos;
            //const vec3 pos1; //结构中的字段不可用const修饰会报错.
        };
    const light lgt = light(vec4(1.0), vec3(0.0)); //结构变量可以用const修饰
    

    attribute:

    attribute变量是全局只读的,它只能在vertex shader中使用,只能与浮点数,向量或矩阵变量组合,
    一般attribute变量用来放置程序传递来的模型顶点,法线,颜色,纹理等数据它可以访问数据缓冲区
    (还记得gl.vertexAttribPointer这个函数吧)

    attribute vec4 a_Position;
    

    uniform:

    uniform变量是全局只读的,在整个shader执行完毕前其值不会改变,他可以和任意基本类型变量组合,
    一般我们使用uniform变量来放置外部程序传递来的环境数据(如点光源位置,模型的变换矩阵等等)
    这些数据在运行中显然是不需要被改变的.

    uniform vec4 lightPosition;
    

    varying:

    varying类型变量是 vertex shader 与 fragment shader 之间的信使,一般我们在 vertex shader 中修改它然后在fragment shader使用它,但不能在
    fragment shader中修改它.

    //顶点着色器
    varying vec4 v_Color;
    void main(){ 
        ...
        v_Color = vec4(1.,1.,1.,1);
    }
    
    //片元着色器
    ...
    varying vec4 v_Color;
    void main() {
      gl_FragColor = v_Color;
    }
    ...
    

    要注意全局变量限制符只能为 const、attribute、uniform和varying中的一个.不可复合.

    函数参数限定符:

    函数的参数默认是以拷贝的形式传递的,也就是值传递,任何传递给函数参数的变量,其值都会被复制一份,然后再交给函数内部进行处理.
    我们可以为参数添加限定符来达到传递引用的目的,glsl中提供的参数限定符如下:

    限定符 说明
    < none: default > 默认使用 in 限定符
    in 复制到函数中在函数中可读写
    out 返回时从函数中复制出来
    inout 复制到函数中并在返回时复制出来

    in 是函数参数的默认限定符,最终真正传入函数形参的其实是实参的一份拷贝.在函数中,修改in修饰的形参不会影响到实参变量本身.

    out 它的作用是向函数外部传递新值,out模式下传递进来的参数是write-only的(可写不可读).就像是一个”坑位”,坑位中的值需要函数给他赋予.
    在函数中,修改out修饰的形参会影响到实参本身.

    inout inout下,形参可以被理解为是一个带值的”坑位”,及可读也可写,在函数中,修改inout修饰的形参会影响到实参本身.

    glsl的函数:

    glsl允许在程序的最外部声明函数.函数不能嵌套,不能递归调用,且必须声明返回值类型(无返回值时声明为void) 在其他方面glsl函数与c函数非常类似.

    vec4 getPosition(){ 
        vec4 v4 = vec4(0.,0.,0.,1.);
        return v4;
    }
    
    void doubleSize(inout float size){
        size= size*2.0  ;
    }
    void main() {
        float psize= 10.0;
        doubleSize(psize);
        gl_Position = getPosition();
        gl_PointSize = psize;
    }
    

    构造函数:

    glsl中变量可以在声明的时候初始化,float pSize = 10.0 也可以先声明然后等需要的时候在进行赋值.

    聚合类型对象如(向量,矩阵,数组,结构) 需要使用其构造函数来进行初始化. vec4 color = vec4(0.0, 1.0, 0.0, 1.0);

    //一般类型
    float pSize = 10.0;
    float pSize1;
    pSize1=10.0;
    ...
    
    //复合类型
    vec4 color = vec4(0.0, 1.0, 0.0, 1.0);
    vec4 color1;
    color1 =vec4(0.0, 1.0, 0.0, 1.0);
    ...
    
    //结构
    struct light {
        float intensity;
        vec3 position;
    };
    light lightVar = light(3.0, vec3(1.0, 2.0, 3.0));
    
    //数组
    const float c[3] = float[3](5.0, 7.2, 1.1);
    

    类型转换:

    glsl可以使用构造函数进行显式类型转换,各值如下:

    bool t= true;
    bool f = false;
    
    int a = int(t); //true转换为1或1.0
    int a1 = int(f);//false转换为0或0.0
    
    float b = float(t);
    float b1 = float(f);
    
    bool c = bool(0);//0或0.0转换为false
    bool c1 = bool(1);//非0转换为true
    
    bool d = bool(0.0);
    bool d1 = bool(1.0);
    

    精度限定:

    glsl在进行光栅化着色的时候,会产生大量的浮点数运算,这些运算可能是当前设备所不能承受的,所以glsl提供了3种浮点数精度,我们可以根据不同的设备来使用合适的精度.

    在变量前面加上 highp mediump lowp 即可完成对该变量的精度声明.

    lowp float color;
    varying mediump vec2 Coord;
    lowp ivec2 foo(lowp mat3);
    highp mat4 m;

    我们一般在片元着色器(fragment shader)最开始的地方加上 precision mediump float; 便设定了默认的精度.这样所有没有显式表明精度的变量
    都会按照设定好的默认精度来处理.

    如何确定精度:

    变量的精度首先是由精度限定符决定的,如果没有精度限定符,则要寻找其右侧表达式中,已经确定精度的变量,一旦找到,那么整个表达式都将在该精度下运行.如果找到多个,
    则选择精度较高的那种,如果一个都找不到,则使用默认或更大的精度类型.

    uniform highp float h1;
    highp float h2 = 2.3 * 4.7; //运算过程和结果都 是高精度
    mediump float m;
    m = 3.7 * h1 * h2; //运算过程 是高精度
    h2 = m * h1; //运算过程 是高精度
    m = h2 – h1; //运算过程 是高精度
    h2 = m + m; //运算过程和结果都 是中等精度
    void f(highp float p); // 形参 p 是高精度
    f(3.3); //传入的 3.3是高精度
    
    

    invariant关键字:

    由于shader在编译时会进行一些内部优化,可能会导致同样的运算在不同shader里结果不一定精确相等.这会引起一些问题,尤其是vertx shader向fragmeng shader传值的时候.
    所以我们需要使用invariant 关键字来显式要求计算结果必须精确一致. 当然我们也可使用 #pragma STDGL invariant(all)来命令所有输出变量必须精确一致,
    但这样会限制编译器优化程度,降低性能.

    #pragma STDGL invariant(all) //所有输出变量为 invariant
    invariant varying texCoord; //varying在传递数据的时候声明为invariant
    

    限定符的顺序:

    当需要用到多个限定符的时候要遵循以下顺序:

    1.在一般变量中: invariant > storage > precision

    2.在参数中: storage > parameter > precision

    我们来举例说明:

    invariant varying lowp float color; // invariant > storage > precision
    
    void doubleSize(const in lowp float s){ //storage > parameter > precision
        float s1=s;
    }
    

    预编译指令:

    以 # 开头的是预编译指令,常用的有:

    #define #undef #if #ifdef #ifndef #else
    #elif #endif #error #pragma #extension #version #line

    比如 #version 100 他的意思是规定当前shader使用 GLSL ES 1.00标准进行编译,如果使用这条预编译指令,则他必须出现在程序的最开始位置.

    内置的宏:

    __LINE__ : 当前源码中的行号.

    __VERSION__ : 一个整数,指示当前的glsl版本 比如 100 ps: 100 = v1.00

    GL_ES : 如果当前是在 OPGL ES 环境中运行则 GL_ES 被设置成1,一般用来检查当前环境是不是 OPENGL ES.

    GL_FRAGMENT_PRECISION_HIGH : 如果当前系统glsl的片元着色器支持高浮点精度,则设置为1.一般用于检查着色器精度.

    实例:

    1.如何通过判断系统环境,来选择合适的精度:

    #ifdef GL_ES //
    #ifdef GL_FRAGMENT_PRECISION_HIGH
    precision highp float;
    #else
    precision mediump float;
    #endif
    #endif
    

    2.自定义宏:

    #define NUM 100
    #if NUM==100
    #endif

    内置的特殊变量

    glsl程序使用一些特殊的内置变量与硬件进行沟通.他们大致分成两种 一种是 input类型,他负责向硬件(渲染管线)发送数据.
    另一种是output类型,负责向程序回传数据,以便编程时需要.

    在 vertex Shader 中:

    output 类型的内置变量:

    变量 说明 单位
    highp vec4 gl_Position; gl_Position 放置顶点坐标信息 vec4
    mediump float gl_PointSize; gl_PointSize 需要绘制点的大小,(只在gl.POINTS模式下有效) float

    在 fragment Shader 中:

    input 类型的内置变量:

    变量 说明 单位
    mediump vec4 gl_FragCoord; 片元在framebuffer画面的相对位置 vec4
    bool gl_FrontFacing; 标志当前图元是不是正面图元的一部分 bool
    mediump vec2 gl_PointCoord; 经过插值计算后的纹理坐标,点的范围是0.0到1.0 vec2

    output 类型的内置变量:

    变量 说明 单位
    mediump vec4 gl_FragColor; 设置当前片点的颜色 vec4 RGBA color
    mediump vec4 gl_FragData[n] 设置当前片点的颜色,使用glDrawBuffers数据数组 vec4 RGBA color

    内置的常量

    glsl提供了一些内置的常量,用来说明当前系统的一些特性. 有时我们需要针对这些特性,对shader程序进行优化,让程序兼容度更好.

    在 vertex Shader 中:

    1.const mediump int gl_MaxVertexAttribs>=8

    gl_MaxVertexAttribs 表示在vertex shader(顶点着色器)中可用的最大attributes数.这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 8 个.

    2.const mediump int gl_MaxVertexUniformVectors >= 128

    gl_MaxVertexUniformVectors 表示在vertex shader(顶点着色器)中可用的最大uniform vectors数. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 128 个.

    3.const mediump int gl_MaxVaryingVectors >= 8

    gl_MaxVaryingVectors 表示在vertex shader(顶点着色器)中可用的最大varying vectors数. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 8 个.

    4.const mediump int gl_MaxVertexTextureImageUnits >= 0

    gl_MaxVaryingVectors 表示在vertex shader(顶点着色器)中可用的最大纹理单元数(贴图). 这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    甚至可以一个都没有(无法获取顶点纹理)

    5.const mediump int gl_MaxCombinedTextureImageUnits >= 8

    gl_MaxVaryingVectors 表示在 vertex Shader和fragment Shader总共最多支持多少个纹理单元. 这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 8 个.

    在 fragment Shader 中:

    1.const mediump int gl_MaxTextureImageUnits >= 8

    gl_MaxVaryingVectors 表示在 fragment Shader(片元着色器)中能访问的最大纹理单元数,这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 8 个.

    2.const mediump int gl_MaxFragmentUniformVectors >= 16

    gl_MaxFragmentUniformVectors 表示在 fragment Shader(片元着色器)中可用的最大uniform vectors数,这个值的大小取决于 OpenGL ES 在某设备上的具体实现,
    不过最低不能小于 16 个.

    3.const mediump int gl_MaxDrawBuffers = 1

    gl_MaxDrawBuffers 表示可用的drawBuffers数,在OpenGL ES 2.0中这个值为1, 在将来的版本可能会有所变化.

    glsl中还有一种内置的uniform状态变量, gl_DepthRange 它用来表明全局深度范围.

    结构如下:

    struct gl_DepthRangeParameters {
     highp float near; // n
     highp float far; // f
     highp float diff; // f - n
     };
     uniform gl_DepthRangeParameters gl_DepthRange;
    

    除了 gl_DepthRange 外的所有uniform状态常量都已在glsl 1.30 中废弃.

    流控制

    glsl的流控制和c语言非常相似,这里不必再做过多说明,唯一不同的是片段着色器中有一种特殊的控制流discard.
    使用discard会退出片段着色器,不执行后面的片段着色操作。片段也不会写入帧缓冲区。

    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 (true)
        discard;
    
    

    内置函数库

    glsl提供了非常丰富的函数库,供我们使用,这些功能都是非常有用且会经常用到的. 这些函数按功能区分大改可以分成7类:

    通用函数:

    下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

    方法 说明
    T abs(T x) 返回x的绝对值
    T sign(T x) 比较x与0的值,大于,等于,小于 分别返回 1.0 ,0.0,-1.0
    T floor(T x) 返回<=x的最大整数
    T ceil(T x) 返回>=等于x的最小整数
    T fract(T x) 获取x的小数部分
    T mod(T x, T y)
    T mod(T x, float y)
    取x,y的余数
    T min(T x, T y)
    T min(T x, float y)
    取x,y的最小值
    T max(T x, T y)
    T max(T x, float y)
    取x,y的最大值
    T clamp(T x, T minVal, T maxVal)
    T clamp(T x, float minVal,float maxVal)
    min(max(x, minVal), maxVal),返回值被限定在 minVal,maxVal之间
    T mix(T x, T y, T a)
    T mix(T x, T y, float a)
    取x,y的线性混合,x*(1-a)+y*a
    T step(T edge, T x)
    T step(float edge, T x)
    如果 x
    T smoothstep(T edge0, T edge1, T x)
    T smoothstep(float edge0,float edge1, T x)
    如果xedge1返回1.0, 否则返回Hermite插值

    角度&三角函数:

    下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

    方法 说明
    T radians(T degrees) 角度转弧度
    T degrees(T radians) 弧度转角度
    T sin(T angle) 正弦函数,角度是弧度
    T cos(T angle) 余弦函数,角度是弧度
    T tan(T angle) 正切函数,角度是弧度
    T asin(T x) 反正弦函数,返回值是弧度
    T acos(T x) 反余弦函数,返回值是弧度
    T atan(T y, T x)
    T atan(T y_over_x)
    反正切函数,返回值是弧度

    指数函数:

    下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

    方法 说明
    T pow(T x, T y) 返回x的y次幂 xy
    T exp(T x) 返回x的自然指数幂 ex
    T log(T x) 返回x的自然对数 ln
    T exp2(T x) 返回2的x次幂 2x
    T log2(T x) 返回2为底的对数 log2
    T sqrt(T x) 开根号 √x
    T inversesqrt(T x) 先开根号,在取倒数,就是 1/√x

    几何函数:

    下文中的 类型 T可以是 float, vec2, vec3, vec4,且可以逐分量操作.

    方法 说明
    float length(T x) 返回矢量x的长度
    float distance(T p0, T p1) 返回p0 p1两点的距离
    float dot(T x, T y) 返回x y的点积
    vec3 cross(vec3 x, vec3 y) 返回x y的叉积
    T normalize(T x) 对x进行归一化,保持向量方向不变但长度变为1
    T faceforward(T N, T I, T Nref) 根据 矢量 N 与Nref 调整法向量
    T reflect(T I, T N) 返回 I - 2 * dot(N,I) * N, 结果是入射矢量 I 关于法向量N的 镜面反射矢量
    T refract(T I, T N, float eta) 返回入射矢量I关于法向量N的折射矢量,折射率为eta

    矩阵函数:

    mat可以为任意类型矩阵.

    方法 说明
    mat matrixCompMult(mat x, mat y) 将矩阵 x 和 y的元素逐分量相乘

    向量函数:

    下文中的 类型 T可以是 vec2, vec3, vec4, 且可以逐分量操作.

    bvec指的是由bool类型组成的一个向量:

    vec3 v3= vec3(0.,0.,0.);
    vec3 v3_1= vec3(1.,1.,1.);
    bvec3 aa= lessThan(v3,v3_1); //bvec3(true,true,true)
    
    方法 说明
    bvec lessThan(T x, T y) 逐分量比较x < y,将结果写入bvec对应位置
    bvec lessThanEqual(T x, T y) 逐分量比较 x <= y,将结果写入bvec对应位置
    bvec greaterThan(T x, T y) 逐分量比较 x > y,将结果写入bvec对应位置
    bvec greaterThanEqual(T x, T y) 逐分量比较 x >= y,将结果写入bvec对应位置
    bvec equal(T x, T y)
    bvec equal(bvec x, bvec y)
    逐分量比较 x == y,将结果写入bvec对应位置
    bvec notEqual(T x, T y)
    bvec notEqual(bvec x, bvec y)
    逐分量比较 x!= y,将结果写入bvec对应位置
    bool any(bvec x) 如果x的任意一个分量是true,则结果为true
    bool all(bvec x) 如果x的所有分量是true,则结果为true
    bvec not(bvec x) bool矢量的逐分量取反

    纹理查询函数:

    图像纹理有两种 一种是平面2d纹理,另一种是盒纹理,针对不同的纹理类型有不同访问方法.

    纹理查询的最终目的是从sampler中提取指定坐标的颜色信息. 函数中带有Cube字样的是指 需要传入盒状纹理. 带有Proj字样的是指带投影的版本.

    以下函数只在vertex shader中可用:

    vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod);
    vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod);
    vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod);
    vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod);
    

    以下函数只在fragment shader中可用:

    vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
    vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
    vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias);
    vec4 textureCube(samplerCube sampler, vec3 coord, float bias);

    在 vertex shader 与 fragment shader 中都可用:

    vec4 texture2D(sampler2D sampler, vec2 coord);
    vec4 texture2DProj(sampler2D sampler, vec3 coord);
    vec4 texture2DProj(sampler2D sampler, vec4 coord);
    vec4 textureCube(samplerCube sampler, vec3 coord);

    官方的shader范例:

    下面的shader如果你可以一眼看懂,说明你已经对glsl语言基本掌握了.

    Vertex Shader:

    uniform mat4 mvp_matrix; //透视矩阵 * 视图矩阵 * 模型变换矩阵
    uniform mat3 normal_matrix; //法线变换矩阵(用于物体变换后法线跟着变换)
    uniform vec3 ec_light_dir; //光照方向
    attribute vec4 a_vertex; // 顶点坐标
    attribute vec3 a_normal; //顶点法线
    attribute vec2 a_texcoord; //纹理坐标
    varying float v_diffuse; //法线与入射光的夹角
    varying vec2 v_texcoord; //2d纹理坐标
    void main(void)
    {
     //归一化法线
     vec3 ec_normal = normalize(normal_matrix * a_normal);
     //v_diffuse 是法线与光照的夹角.根据向量点乘法则,当两向量长度为1是 乘积即cosθ值
     v_diffuse = max(dot(ec_light_dir, ec_normal), 0.0);
     v_texcoord = a_texcoord;
     gl_Position = mvp_matrix * a_vertex;
    }

    Fragment Shader:

    precision mediump float;
    uniform sampler2D t_reflectance;
    uniform vec4 i_ambient;
    varying float v_diffuse;
    varying vec2 v_texcoord;
    void main (void)
    {
     vec4 color = texture2D(t_reflectance, v_texcoord);
     //这里分解开来是 color*vec3(1,1,1)*v_diffuse + color*i_ambient
     //色*光*夹角cos + 色*环境光
     gl_FragColor = color*(vec4(v_diffuse) + i_ambient);
    }
    展开全文
  • GLSL教程】(一)图形流水线

    万次阅读 多人点赞 2011-07-26 15:52:00
    这是一些列来自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实现图像处理

    万次阅读 2015-05-06 21:25:10
     这些操作其本质上是属于图像处理的东西,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的优势,对于图像的处理将会有速度上的很大提升。




    展开全文
  • OpenGL shader GLSL 中文手册

    万次阅读 2019-02-20 15:58:12
    GLSL 中文手册 基本类型: 类型 说明 void 空类型,即不返回任何值 bool 布尔类型 true,false int 带符号的整数 signed integer float 带符号的浮点数 floating scalar vec2, vec3, vec4.....
  • GLSL(着色器语言)

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

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

    千次阅读 2017-04-20 13:30:46
    GLSL. 语法基础
  • GLSL入门

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

    万次阅读 2015-03-19 15:05:37
    权威官方文档:https://www.opengl.org/documentation/glsl/ 权威民间金典入门教程:http://blog.csdn.net/racehorse 一个详细完整的GLSL代码:http://www.ituring.com.cn/article/851 GLSL简介和所有内置...
  • GLSL的调试方法

    千次阅读 2016-04-27 10:20:08
    vs通过CPU模拟,fs将值输出到屏幕上。
  • GLSL 数据精度

    千次阅读 2013-03-28 16:25:56
    1. 默认精度 顶点着色器中默认精度 precision highp float; precision highp int; precision lowp sampler2D; precision lowp samplerCube; 像素着色器中默认精度 precision mediump int;...precision lo
  • GLSL的texture2D函数

    万次阅读 2013-12-11 22:44:58
    最近在学习GLSL着色语言,对于texture2D这个函数始终有点搞不明白,今天终于搞清楚了 texture2D的第一个参数是采样器(可以是sampler1D sampler2D sampler3D samplerCube sampler1DShadow sampler2DShadow) 由于...
  • OpenGL 与 GLSL 版本

    千次阅读 2015-03-07 11:49:43
    来自:https://github.com/mattdesl/lwjgl-basics/wiki/GLSL-Versions ...You can use the #version command as the first line of your shader to specify GLSL version: #version 120 void main() { gl_FragC
  • GLSL常用内置函数汇总

    千次阅读 2017-08-05 10:57:34
    all:在所有为真的时候,返回为真; any:在任一变量为真的时候,返回为真; dot:向量点乘 cross:向量的叉积 ceil(x):向上取整 如:ceil(3.5) 值为4 floor(x):向下取整 如:floor(3.5) 值为3 ...
  • 对于GLSL,随机/杂点函数

    千次阅读 2017-10-25 21:43:38
    对于GLSL,随机/杂点函数 对于非常简单的pseudorandom-looking内容,我使用了在互联网上找到的oneliner: float rand(vec2 co){ return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); } ...
  • https://github.com/mattdesl/lwjgl-basics/wiki/GLSL-Versions ...GLSL Versions OpenGL Version GLSL Version 2.0 110 2.1 120 3.0 130 3.1
  • 前辈们,在OpenGL GLSL编程中,应用程序如何将大数据(5MB~10MB大小)传递给Shader中使用? 谢谢大家~
  • 在 Unity3d 中使用 GLSL

    千次阅读 2015-08-20 15:28:16
    首先要设置Unity为OpenGL模式 在Unity的快捷方式下添加如下 "C:\Program Files (x86)\Unity\Editor\Unity.exe" -force-opengl ...Shader "GLSL/Test001" { Properties { _Color("Main Color",Color)=(1,0,0
  • [软件开发] glsl代码编辑器

    千次阅读 2018-05-17 12:37:29
    本人根据自己的需求,借助Qt开发了一款形式上仿照nodepad++的glsl代码编辑器,支持glsl语法高亮、glsl内建词自动补齐、glsl内建词简单查询和其他编辑器常用功能,分享给有需要的朋友。 ui样式这些都是根据本人喜好...
1 2 3 4 5 ... 20
收藏数 11,782
精华内容 4,712
关键字:

glsl