使用GLSL画点,画线,画面,与原来使用glBegin(), glEnd()的方式有类似的地方,原来方式api比较多,GLSL采用的数组一次传送,程序的本质还是坐标点的设计与确认,只要知道图怎么画,哪种方式差异不大,本章主要介绍:

1. 正弦函数的基本画法

2. 键盘的控制

3. uniform变量的用法

4. 正弦波叠加为方波的GLSL实现

GLSL画这些基本的类型是,使用的函数主要是glDraw*系列的函数,这里再说一下:

void  glDrawArrays (GLenum mode, GLint first, GLsizei count);

mode与老的方式一致,有以下类型,画点GL_POINTS,画线 GL_LINES,顺连线段GL_LINE_STRIP,回环线段GL_LINE_LOOP,三角形GL_TRIANGLES,GL_TRIANGLE_STRIP,GL_TRIANGLE_FAN,四边形GL_QUADS,GL_QUAD_STRIP,多边形GL_POLYGON。first为0即可,count表示要绘制顶点的个数,这个上章说的比较多。

点面的画法的例子比较多,请大家自己去练(可以找老的程序改用GLSL方式实现来练手)。

说明:严格的说,以上关于新老绘制方式的描述并不准确,openGL绘制方式一直在改进,这里主要表达的意思是大家需区分使用shader的方式绘制与直接绘制方式的异同,详细的绘制演进过程,可以看一下这篇文章的介绍:

基本图形绘制方式比较

上一章的helloworld程序非常简单,这里通过正弦波的画法来实现一个稍微复杂一点的shader程序,让大家尽快感受shader编程。

1. 正弦波绘制

正弦波公式y = sin(x ) ,公式大家都熟悉,怎么画出来呢?这里我确定了以下几个参数来画正弦波:

1. sampleCnt:采样点个数,openGL画东西都采用逼近的方式,采样点越多,正弦波就越精细。

2. factor:用来控制正弦波的频率,如sin(2x ),sin(3x ) 等。

3. amplitude:振幅,用来控制正弦波的振幅,如3sin(2x )。

4. rangeL:我们要把正弦波映射到[-1.0,1.0]的范围内,否则画出来的正弦波都看不到。

5. rangeR:如传的-pi~pi的范围,会把这个范围的正弦波映射到[-1.0,1.0]范围内。

注意:shader里面y坐标统一乘了0.9,主要是避免图形顶到边框,代码如下,可以改参数效果:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200typedef struct
{GLfloat x;GLfloat y;
}Point;
static const GLchar * vertex_source ="#version 330 core\n""layout (location = 0) in vec2 position;\n""void main()\n""{\n""gl_Position = vec4(position.x,position.y*0.9,0.0,1.0);\n""}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{const GLchar * shaderSource[] = {source};GLuint shader = glCreateShader(type);glShaderSource(shader, 1, shaderSource, 0);glCompileShader(shader);glAttachShader(program, shader);
}
void init()
{GLuint program = glCreateProgram();loadShader(program,GL_VERTEX_SHADER,vertex_source);glLinkProgram(program);glUseProgram(program);glClearColor(0.5f,0.5f, 1.0f, 1.0f);
}
Point * createSinArray(GLint sampleCnt,GLfloat factor,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{int i = 0;GLfloat range = rangeR-rangeL;Point * array = NULL;if((sampleCnt <= 4) || (rangeR <= rangeL)){printf("param error sampleCnt:%d rangeR:%f rangeL:%f\n",sampleCnt,rangeL,rangeR);return NULL;}   array = (Point * )malloc(sampleCnt * sizeof(Point));for(i = 0;i<sampleCnt;i++){/* x坐标按采样点均匀的分布在[-1.0,1.0]的范围内*/array[i].x = (2.0*i-sampleCnt)/sampleCnt;/* y坐标考虑到了振幅,频率因素的影响*/array[i].y = amplitude*sin(factor*(rangeL+i*range/sampleCnt));//printf("array[%d]:%f-%f\n",i,array[i].x,array[i].y);}  return array;
}
void deletSinArray(Point * array)
{if(array){free(array);}
}
void display()
{int i = 0;glClear(GL_COLOR_BUFFER_BIT);Point * sinaArray = createSinArray(SAMPLE_CNT,1.0,1.0,-3*PI,3 * PI);if( sinaArray){glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,sizeof(Point), (GLvoid *)sinaArray);glEnableVertexAttribArray(0);   glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);deletSinArray(sinaArray);}glFlush();}
int main(int argc, char * argv[])
{glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA);glutInitWindowPosition(200, 200);glutInitWindowSize(300,300);glutCreateWindow("article3");glewInit();init();glutDisplayFunc(display);glutMainLoop();return 0;
}

结果如下:

2. 按键捕获

每次改参数需要编译才能看效果还是很不方便,下面的例子加入了键盘控制,并加入了正弦波合成方波的处理,可以使用箭头键移动正弦波,使用上下箭头进行振幅调整,使用+,-号来调整正弦波叠加的次数。

傅里叶函数分解方波公式:

f(y) = 4/PI * (sinx+ sin3x/3 + sin5x/5 + ...);

实际程序里面公式为:

f(y) = sinx+ sin3x/3 + sin5x/5 + ...

学习GLSL的同时,顺便来熟悉一下傅里叶函数,想起一句话,只要努力,弯的也能掰成直的(咳,不是我说的,有兴趣的可以搜一下傅里叶掐死教程)。键盘输入捕获主要使用一下两个函数:

void glutKeyboardFunc(void(*func)(unsigned char key,int x,int y));
void glutSpecialFunc(void (*func)(int key,int x,int y));

glutKeyboardFunc能捕获普通的按键(数字,字母),而glutSpecialFunc用来捕获方向键,F10等特殊键,这两句话加到glutMainLoop之前就可以了。

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200typedef struct
{GLfloat x;GLfloat y;
}Point;
static const GLchar * vertex_source ="#version 330 core\n""layout (location = 0) in vec2 position;\n""uniform mat4 matrix;\n""void main()\n""{\n""gl_Position = vec4(position.x,position.y*0.9,0.0,1.0);\n""}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{const GLchar * shaderSource[] = {source};GLuint shader = glCreateShader(type);glShaderSource(shader, 1, shaderSource, 0);glCompileShader(shader);glAttachShader(program, shader);
}
void init()
{GLuint program = glCreateProgram();loadShader(program,GL_VERTEX_SHADER,vertex_source);glLinkProgram(program);glUseProgram(program);glClearColor(0.5f,0.5f, 1.0f, 1.0f);
}
Point * createSinArray(GLint sampleCnt,GLfloat factor,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{int i = 0;GLfloat range = rangeR-rangeL;Point * array = NULL;if((sampleCnt <= 4) || (rangeR <= rangeL)){printf("param error sampleCnt:%d rangeR:%f rangeL:%f\n",sampleCnt,rangeL,rangeR);return NULL;}   array = (Point * )malloc(sampleCnt * sizeof(Point));for(i = 0;i<sampleCnt;i++){array[i].x = (2.0*i-sampleCnt)/sampleCnt;array[i].y = amplitude*sin(factor*(rangeL+i*range/sampleCnt));//printf("array[%d]:%f-%f\n",i,array[i].x,array[i].y);}  return array;
}
void deletSinArray(Point * array)
{if(array){free(array);}
}
/*
* @param sinCnt:正弦波叠加次数
* @param sampleCnt:采样点数,越多,正弦波越精细
* @param amplitude:叠加后的振幅
* @param rangeL:左边界坐标
* @param rangeR:右边界坐标
*/
Point * createSquareWave(GLint sinCnt,GLint sampleCnt,GLfloat amplitude,GLfloat rangeL,GLfloat rangeR)
{int i = 0,j = 0;Point * array = (Point * )calloc(sampleCnt,sizeof(Point));for(i = 0;i<sinCnt;i++){int f = 2*i+1;/* 依次叠加正弦波,注意频域为奇数*/Point * sinaArray = createSinArray(sampleCnt,1.0*f,1.0/f,rangeL,rangeR);for( j = 0;j<sampleCnt;j++){array[j].x = sinaArray[j].x;array[j].y += (sinaArray[j].y*amplitude);       }deletSinArray(sinaArray);}return array;
}
int g_sinCnt = 3;
GLfloat g_rangeL = -3*PI,g_rangeR = 3 * PI;
GLfloat g_amplitud = 1.0;
void display()
{glClear(GL_COLOR_BUFFER_BIT);Point * squareWaveArray = createSquareWave(g_sinCnt,SAMPLE_CNT,g_amplitud,g_rangeL,g_rangeR);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE,sizeof(Point), (GLvoid *)squareWaveArray);glEnableVertexAttribArray(0);       glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);  deletSinArray(squareWaveArray);glFlush();
}
void keyboard(unsigned char key, int x, int y)
{switch(key){case '-':if( g_sinCnt > 1) g_sinCnt--;break;case '=':case '+':if( g_sinCnt < 50) g_sinCnt++;break;default:break;}printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n",g_sinCnt,g_rangeL,g_rangeR,g_amplitud);glutPostRedisplay();
}
void specialKey(GLint key,GLint x,GLint y)
{switch(key){case GLUT_KEY_UP:if( g_amplitud < 2)     g_amplitud += 0.1;break;case GLUT_KEY_DOWN:if( g_amplitud > 0.3)   g_amplitud -= 0.1;break;case GLUT_KEY_LEFT:     g_rangeL -= 0.1;g_rangeR -= 0.1;break;case GLUT_KEY_RIGHT:g_rangeL += 0.1;g_rangeR += 0.1;break;default:break;}printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n",g_sinCnt,g_rangeL,g_rangeR,g_amplitud);glutPostRedisplay();
}
int main(int argc, char * argv[])
{glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA);glutInitWindowPosition(200, 200);glutInitWindowSize(300,300);glutCreateWindow("article3");glewInit();init();glutDisplayFunc(display);glutKeyboardFunc(keyboard);glutSpecialFunc(specialKey);glutMainLoop();return 0;
}

大家可以按方向键调整位置和振幅,通过+-键控制叠加次数。

叠加次数4次效果:

叠加次数50次效果:

方波波形不完善的原因应该与采样精度不足、叠加次数不够及单浮点精度不足有关。

3. uniform变量

以上程序问题大家应该看到了,效率太低,根本是用CPU来画正弦函数,计算基本在CPU上完成的,GPU的浮点计算与并行计算的优势完全没有发挥出来,有什么办法可以把浮点运算挪到GPU里去算呢?这个实现不复杂,有一个问题需先解决,除了向GPU传递顶点坐标外,我们还需要向GPU传递控制信息,如上面例子的方向键与+-的控制信息,这个可以通过uniform变量实现。

uniform变量使用流程如下:

1. shader程序定义uniform变量

2. 客户程序使用glGetUniformLocation函数获取uniform变量的索引(句柄、描述符)

3. 使用 glUniform**(程序里用 glUniform1f与 glUniform1i就可以了)把uniform变量值传送到shader程序,shader就可以用这个值了。

不同类型的glUniform**函数参数差异还挺大的,都不用记,可以直接查API用法:

https://www.khronos.org/registry/OpenGL-Refpages/gl4/

注意:uniform与in的区别需要弄清楚,红宝书2.3.2节说的比较详细。uniform 特点是所有shader都可以使用,且在shader里不可修改,in重数据传递,uniform 主要用于控制传递。

#include <GL/glew.h>
#include <GL/glut.h>
static const GLchar * vertex_source ="#version 330 core\n""uniform float translate_x;\n""uniform float translate_y;\n""layout (location = 0) in vec2 position;\n""void main()\n""{\n""gl_Position = vec4(position.x+translate_x,position.y+translate_y,0.0,1.0);\n""}\0";
void loadShader(GLuint program,GLuint type,const GLchar * source)
{const GLchar * shaderSource[] = {source};GLuint shader = glCreateShader(type);glShaderSource(shader, 1, shaderSource, 0);glCompileShader(shader);glAttachShader(program, shader);
}void init()
{GLfloat translate_x_index,translate_y_index;GLuint program = glCreateProgram();loadShader(program,GL_VERTEX_SHADER,vertex_source);glLinkProgram(program);glUseProgram(program);/* 获取uniform变量索引(位置),注意名称要和shader中的保持一致*/translate_x_index = glGetUniformLocation(program, "translate_x");translate_y_index = glGetUniformLocation(program, "translate_y");/* 通过索引把信息传到GPU,供shader使用*/glUniform1f(translate_x_index,-0.6);glUniform1f(translate_y_index,0.3);glClearColor(0.5f,0.5f, 1.0f, 1.0f);
}
void display()
{glClear(GL_COLOR_BUFFER_BIT);GLfloat vertices[] = {0.0, 0.0,0.5,0.5,0.5,0.0};glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid *)vertices);glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);glFlush();
}
int main(int argc, char * argv[])
{glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA);glutInitWindowPosition(200, 200);glutInitWindowSize(300,300);glutCreateWindow("HelloWord");glewInit();init();glutDisplayFunc(display);glutMainLoop();return 0;
}

可以看到相比第二章的例子,shader 里增加了两个 uniform 变量, init 时先通过变量名获取 uniform 变量位置(一般叫位置,有的也称为索引),再通过位置   把数据传送给 GPU ,整体过程还是挺简单的,大家可以改 glUniform1f  传的值看效果,效果如下:

glUniform*函数用来更新uniform变量 ,原型有很多,总的来说函数原型分成下3大类

1. 变量操作: glUniform{1|2|3|4}{f|i|d|ui}(GLint location,TYPE value);

2. 数组操作:glUniform{1|2|3|4}{f|i|d|ui}v(GLint location,GLsizei count,const TYPE * value);

3. 矩阵数组操作: glUniformFloatMatrix{2|3|4}fv(GLint location,GLsizei count,GLboolean transpose,const TYPE * value);

其中f=float,i = int,d=double,ui=unsigned int ,TYPE为对应的类型,以v结尾的都是数组类型。下面列的都是4维的函数原型:

void glUniform4i(GLint location,GLint v0,GLint v1,GLint v2,GLint v3);
void glUniform4f(GLint location,GLfloat v0,GLfloat v1,GLfloat v2,GLfloat v3);
void glUniform4iv(GLint location,GLsizei count,const GLint *value);
void glUniform4fv(GLint location,GLsizei count,const GLfloat *value);
void glUniformMatrix4fv(GLint location,GLsizei count,GLboolean transpose,const GLfloat *value);

矩阵操作只提供了数组方式的操作,矩阵方式有参数transpose参数,为true时,以行主序方式读入(c语言数组方式),false为列主序方式读取(shader中,矩阵默认以列为主序,自己组矩阵的时候需注意)。

Uniform变量数组的操作例子如下:

mat4 model_matrix[8] = {...};
glUniformMatrix4fv(render_model_matrix_loc, 8, GL_FALSE, model_matrix[0]);

注意:count的值需与矩阵数组一致,否则传送的数据不对,红宝书第三章例子ch03_drawcommands.cpp中,count的值(应为1,程序为4)不正确导致程序一片黑。

model_matrix = vmath::translation(-3.0f, 0.0f, -5.0f);
glUniformMatrix4fv(render_model_matrix_loc, 4, GL_FALSE, model_matrix);
glDrawArrays(GL_TRIANGLES, 0, 3);

4. 正弦波叠加的GLSL实现

通过uniform变量传送控制信息到shader中,就可以使用shader来实现本章的正弦叠加的例子。对于复杂的例子,使用shader文件的方式更好阅读,为方便大家编译,本章的例子先用notepad++写好,再复制上来的,阅读的时候,大家可以把分号去掉,比较方便。

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <GL/glew.h>
#include <GL/glut.h>
#define PI          3.14159265
#define SAMPLE_CNT  200
static const GLchar * vertex_source =
"#version 330 core                                                                                      \n"
"layout (location = 0) in float vertexSerial;                                                           \n"
"uniform int g_sinCnt;                                                                                  \n"
"uniform float g_rangeL;                                                                                \n"
"uniform float g_rangeR;                                                                                \n"
"uniform float g_amplitud;                                                                              \n"
"const int sampleCnt=200;                                                                               \n"
"                                                                                                       \n"
"vec2 createSinPostion(float posIdex,float factor,float amplitude, float rangeL, float rangeR)          \n"
"{                                                                                                      \n"
"    vec2 sinPos;                                                                                       \n"
"    float range = rangeR - rangeL;                                                                     \n"
"                                                                                                       \n"
"    sinPos.x = (2.0 * posIdex - sampleCnt)/sampleCnt;                                                  \n"
"    sinPos.y = amplitude * sin(factor * (rangeL + posIdex * range / sampleCnt));                       \n"
"                                                                                                       \n"
"    return sinPos;                                                                                     \n"
"}                                                                                                      \n"
"                                                                                                       \n"
"vec2 createSquareWave(float posIdex,int sinCnt, float amplitude, float rangeL, float rangeR)           \n"
"{                                                                                                      \n"
"    vec2 SquareWarvePos, sinPos;                                                                       \n"
"    int i = 0;                                                                                         \n"
"                                                                                                       \n"
"    for(i = 0;i<sinCnt;i++)                                                                            \n"
"    {                                                                                                  \n"
"        int f = 2 * i + 1;                                                                             \n"
"                                                                                                       \n"
"        sinPos = createSinPostion(posIdex, 1.0 * f, 1.0 / f, rangeL, rangeR);                          \n"
"        SquareWarvePos.x = sinPos.x;                                                                   \n"
"        SquareWarvePos.y += (sinPos.y * amplitude);                                                    \n"
"    }                                                                                                  \n"
"                                                                                                       \n"
"    return SquareWarvePos;                                                                             \n"
"}                                                                                                      \n"
"                                                                                                       \n"
"void main()                                                                                            \n"
"{                                                                                                      \n"
"    vec2 SquareWarvePos  = createSquareWave(vertexSerial,g_sinCnt,g_amplitud,g_rangeL,g_rangeR);       \n"
"    gl_Position = vec4(SquareWarvePos,0.0,1.0);                                                        \n"
"}                                                                                                      \n"
"\0";
void loadShader(GLuint program, GLuint type, const GLchar * source)
{const GLchar * shaderSource[] = {source};GLuint shader = glCreateShader(type);glShaderSource(shader, 1, shaderSource, 0);glCompileShader(shader);  glAttachShader(program, shader);
}
/* uniform控制变量的位置定义*/
GLint sinCntIdx;
GLfloat rangeLIdx,rangeRIdx,amplitudIdx;
/* uniform控制变量的值定义*/
GLint g_sinCnt = 3;
GLfloat g_rangeL = -3 * PI,g_rangeR = 3 * PI,g_amplitud = 1.0;
void init()
{GLuint program = glCreateProgram();loadShader(program, GL_VERTEX_SHADER, vertex_source);glLinkProgram(program);glUseProgram(program);/* 获取shader中uniform变量位置*/sinCntIdx = glGetUniformLocation(program, "g_sinCnt");rangeLIdx = glGetUniformLocation(program, "g_rangeL");rangeRIdx = glGetUniformLocation(program, "g_rangeR");amplitudIdx = glGetUniformLocation(program, "g_amplitud");glClearColor(0.5f, 0.5f, 1.0f, 1.0f);
}
void display()
{glClear(GL_COLOR_BUFFER_BIT);/* 向shader中uniform变量传送值*/glUniform1i(sinCntIdx,g_sinCnt);   glUniform1f(rangeLIdx,g_rangeL);glUniform1f(rangeRIdx,g_rangeR);glUniform1f(amplitudIdx,g_amplitud);/* 顶点的坐标由shader自己生成,但需告知shader点的索引*/GLfloat vertexSerial[SAMPLE_CNT];for(int i = 0;i<SAMPLE_CNT;i++){vertexSerial[i] = i;}glVertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, sizeof(GLfloat), (GLvoid *)vertexSerial);glEnableVertexAttribArray(0);glDrawArrays(GL_LINE_STRIP, 0, SAMPLE_CNT);glFlush();
}
void keyboard(unsigned char key, int x, int y)
{switch (key){case '-':if (g_sinCnt > 1) g_sinCnt--;break;case '=':case '+':if (g_sinCnt < 50) g_sinCnt++;break;default:break;}printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n", g_sinCnt, g_rangeL, g_rangeR, g_amplitud);glutPostRedisplay();
}
void specialKey(GLint key, GLint x, GLint y)
{switch (key){case GLUT_KEY_UP:if (g_amplitud < 2)    g_amplitud += 0.1;break;case GLUT_KEY_DOWN:if (g_amplitud > 0.3)    g_amplitud -= 0.1;break;case GLUT_KEY_LEFT:g_rangeL -= 0.1;g_rangeR -= 0.1;break;case GLUT_KEY_RIGHT:g_rangeL += 0.1;g_rangeR += 0.1;break;default:break;}printf("g_sinCnt:%d g_rangeL:%f g_rangeR:%f g_amplitud:%f\n", g_sinCnt, g_rangeL, g_rangeR, g_amplitud);glutPostRedisplay();
}
int main(int argc, char * argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGBA);glutInitWindowPosition(200, 200);glutInitWindowSize(300, 300);glutCreateWindow("article3");glewInit();init();glutDisplayFunc(display);glutKeyboardFunc(keyboard);glutSpecialFunc(specialKey);glutMainLoop();return 0;
}

运行的结果(效果)与客户端实现是一致的,效率上应该会更高一些,这里就不贴截图了。

大家可以看到使用shader语言来实现正弦叠加函数,shader的编写与客户端代码编写最大的不同是shader处理的是单个点坐标, 程序中去掉了采样点循环,程序更简洁,语法和C语言基本一致,客户端代码移植到shader中去,只需做简单的修改即可。

客户度向GPU只传送了采样点的序列,传送的数据小很多,如果使用vbo与vao,display传送数据部分,可以进一步简化和提高效率。

注意:

1. 目前调试shader程序没什么好的办法,红宝书shader的例子都很简短,调试长一点的shader,请大家先把loadShader里的错误处理加上。

2. GLint,GLfloat等是openGL客户端的变量类型,shader里面用的都是int,float等,与c类似的未封装类型。

3. GLSL内置了很多函数,上述的sin函数就是内置的,内置函数原型用的时候可以查手册(红宝书附录C),避免与c、c++的库函数搞混了。

openGL之glsl入门3--正弦函数叠加为方波相关推荐

  1. openGL之glsl入门6--画三维图魔方、圆柱体

    这一章介绍坐标变换与矩阵相关内容,对应红宝书第5章内容,并通过两个简单的例子展示矩阵变换的效果. 1. 坐标变换 变换的类型有多种,包括视图.模型.投影.视口变换等,概念可以参照红宝书5.1章节,概念 ...

  2. OpenGl计算机图形学入门

    OpenGl计算机图形学入门 前言 一.OpenGl及GLSL 二.语言 1.C++及C++应用程序 (1)C++ (2)C++应用程序 三.库 1.GLFW 窗口管理库 2.GLEW 扩展库 3.G ...

  3. android自定义美颜相机完整程序,Android OpenGL ES从入门到进阶(一)—— 五分钟开发一款美颜相机...

    源码链接:https://github.com/smzhldr/AGLFramework 一.前言 商店里有数十款的美颜相机类产品,其实现原理基本上都是以OpenGL ES为核心的特效处理,大神可以忽 ...

  4. OpenGL 与 GLSL 版本号

    来自:https://github.com/mattdesl/lwjgl-basics/wiki/GLSL-Versions You can use the #version command as t ...

  5. opengl编程从入门到精通-hello,window

    窗口 程序中使用的glad.c和glad.h源码: 链接: https://pan.baidu.com/s/1TmtM7O8J4aTnfP6elIgs7g 密码: g5go github源码仓库 op ...

  6. OpenGL 高级GLSL(Advanced GLSL)

    OpenGL 高级GLSL OpenGL 高级GLSL简介 GLSL的内建变量 顶点着色器变量 片段着色器变量 接口块 Uniform缓冲对象 Uniform块布局 使用Uniform缓冲 一个简单的 ...

  7. OpenGL编程轻松入门(一)

    OpenGL编程轻松入门(一) (由同事黄燕创作)   本文介绍了有关OpenGL的基本知识,主要涉及颜色.绘制几何体.坐标变换.堆栈操作.显示列表.光照和材质.纹理映射.特殊效果.曲面和曲线的绘制. ...

  8. 实验一OpenGL图形编程入门

    实验一OpenGL图形编程入门 一. 实验目的 1.了解和掌握OpenGL的安装. 2.掌握一个简单的基于OpenGL的C++程序结构. 3.掌握Win32程序框架. 4.掌握OpenGL中若干基本图 ...

  9. android 美颜相机开发,Android OpenGL ES从入门到进阶(一)—— 五分钟开发一款美颜相机...

    源码链接:https://github.com/smzhldr/AGLFramework 一.前言 商店里有数十款的美颜相机类产品,以及像抖音,唱吧之类带有视频的软件,功能很强大,其实现原理基本上都是 ...

  10. 【OpenGL ES】入门及绘制一个三角形

    本文首发于个人博客:Lam's Blog - [OpenGL ES]入门及绘制一个三角形,文章由MarkDown语法编写,可能不同平台渲染效果不一,如果有存在排版错误图片无法显示等问题,烦请移至个人博 ...

最新文章

  1. 鸿蒙可以和安卓抗衡吗,鸿蒙手机系统正式登场!继承EMUI的衣钵,能抗衡安卓系统吗?...
  2. HTTP协议(5)HTTP请求和响应
  3. NYOJ 2 括号配对问题
  4. 使用mongoose来创建嵌入式websocket客户端和http客户端
  5. Android开发笔记(四十二)Broadcast的生命周期
  6. 数据体系建设的开端,该如何规划平台? 1
  7. 云服务器的IT价值与部署分析
  8. 【Linux】03 文件权限
  9. 疫情地图 | 低代码制作全国重点管控地区行政区地图(截至4月16日)
  10. 【51单片机】单片机仿真软件Proteus 8.7破解和汉化教程(附下载地址)
  11. 原生拦截WebView页面下载链接跳转空白页问题
  12. 关于门控时钟的毛刺解决
  13. ps去除图片中的文字、图层锁定不能解开问题
  14. CentOS 查看登陆成功和登陆失败日志
  15. CentOS 7 LVM创建与使用
  16. 简笔画花边边框超简单_花边简笔画简单又漂亮 手抄报的边框图片大全
  17. [转载] 真正可用的使用T5577卡复制4100卡_ID卡复制操作流程
  18. CSS3动画效果-animate.css
  19. java线程堆栈nid.tid_java排查一个线上死循环cpu暴涨的过程分析
  20. iPhone和Android的WEB应用开发详解

热门文章

  1. 2. java压缩tar文件
  2. 翻转课堂计算机语言逻辑性,通达翻转课堂模式下大学数学教学模式的探讨
  3. 乔布斯在斯坦福大学的演讲
  4. android模拟器超级root,android模拟器root,avd root,emulator root教程
  5. 【整理】显微镜下人体细胞视频合集
  6. 一本通题解——1436:数列分段II
  7. python实践周总结_Python 一周总结
  8. 教你用python画动态爱心表白
  9. Ordinal Numbers
  10. matlab初值随机扰动,GRAPES区域集合预报系统模式不确定性的随机扰动技术研究