1.OpenGL简介
1.1简介
-
应用程序编程接口
-
对图形硬件设备特性进行访问的软件库
-
与硬件无关,可以通过软件的方式实现OpenGL接口
-
不包含任何执行窗口任务,或者处理用户输入的函数
-
不提供三维物体,几何图元来创建三维空间物体
-
API是过程性
-
可以软件实现,也可以硬件实现
1.2 应用
-
视频 图形 图片处理
-
2D/3D游戏引擎开发
-
科学可视化,医学软件的开发
-
CAD(计算机辅助技术)
-
虚拟实境(AR VR)
-
AI人工智能
1.3 OpenGL 与OpenGL ES
OpenGL ES是一个在移动平台上能够支持 OpenGL 最基本功能的精简规范。
1.4 OpenGL ES Android 支持版本
1.5 OpenGL绘制流程
- 从OpenGL的几何图元中设置数据,用于构建形状
- 用不同的着色器对输入的图元数据执行计算操作,判断位置,颜色以及其他渲染属性
- 输入图元的数学描述 转换为与屏幕位置对应的像素片元,也称光栅化
- 针对光栅化过程产生的每个片元,执行 片元着色器,从而决定这个片元的最终颜色和位置
- 如果有必要 可以对片元执行一些额外操作(判断片元对应的对象是否可见,或者将片元的颜色与当前屏幕位置的颜色进行融合。)
2.GLSL
2.1 着色器介绍
-
顶点着色器------- 将虚拟空间三维坐标 映射到 屏幕显示二维坐标 +Z-buffer深度信息
模型如下:
-
片元着色器 --------计算每个像素的颜色和其它属性 (如光照值,贴图,阴影)
模型如下:
2.2 七大数据类型
-
标量----基本数据类型
- int 32位
- float 32位
- bool 8位
-
向量----常用于表示颜色,纹理,坐标
-
矩阵----常用于位移,旋转,缩放操作
- 类型 mat2 mat3 mat4
- 填充顺序从下向下,从左往右
-
采样器----纹理采样 (用在片元着色器,在宿主(java)中初始化)
- 3类采样器 sampler2D sampler3D samplerCube(立体贴图,需要手动开启功能)
-
结构体----简化运算
struct info { vec3 a; vec2 b; vec3 c }
-
数组----float f[]
-
空类型 ----void
2.3 类型修饰符
- attribute (顶点着色器特有,一般用于各个顶点各不相同的量)
- uniform (一般用于对于3D物体中所有顶点都相同的量)
- varing (一般用于顶点着色器传递到片元着色器的量)
- const (常量)
- in/out/inout (输入输出修饰符,默认in)
2.4 浮点精度
precision | 描述 | 位数(位) |
---|---|---|
lowp | 低精度 | 8 |
mediump | 中精度 | 10 |
highp | 高精度 | 16 |
3.绘制及纹理映射
3.1绘制方式
绘制方式 | 说明 |
---|---|
GL_POINTS | 点类下唯一的绘制方式,用来绘制点。 |
GL_LINES | 将着色器传入的顶点按顺序两个一组来绘制成线段 |
GL_LINES_STRIP | 按照顶点顺序连接顶点。(不封口) |
GL_LINES_LOOP | 按照顶点顺序连接顶点,并将第一个顶点和最后一个顶点相连。(封口) |
GL_TRIANGLES | 按照顶点顺序每3个点组成三角形进行绘制 |
GL_TRIANGLE_STRIP | 顶点按照顺序依次组织成三角形进行绘制, 最后实际形成的是一个三角形带。若有 |
GL_TRIANGLE_FAN | 将第一个点作为中心点其他点作为边缘点,绘制一系列组成扇形的相邻三角形 |
3.2 纹理映射
3.2.1 映射原理
- 为顶点指定纹理坐标
- 通过纹理坐标确认纹理区域
- 将选定区域根据纹理坐标映射到图元上
3.3.2 注意
- 坐标范围 0-1
- 纹理图片 宽高必须是2^n
3.3.3纹理拉伸和截取
//1=============================================================
重复拉伸
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
,GLES20.GL_TEXTURE_WRAP_S
,GLES20.GL_REPEAT);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
,GLES20.GL_TEXTURE_WRAP_T
,GLES20.GL_REPEAT);
//2=================================================================
截取拉伸
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
3.3.4纹理采样
最近点采样 | 线性采样 | |
---|---|---|
基本原理 | 对应像素点 | 加权平均 |
优点 | 简单 采样快 | 线性平滑 |
缺点 | 将小图映射到大图上,会产生锯齿 | 容易造成线条模糊 |
【1】最近点采样
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D
,GLES20.GL_TEXTURE_MAG_FILTER
,GLES20.GL_NEAREST);
【2】线性采样
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
MIN与MAG采样
当纹理图中的一个像素对应到待映射图元上的多个片元时,采用MAG采样;反之则采用MIN采样。 通俗版:纹理比图元小,采用MAG采样纹理设置。纹理比图元大,用MIN采样纹理设置 配合:MIN与最近点,MAG与线性采样
4.光照
-
环境光 = 材质反射系数*环境光强度
-
散射光 = 材质反射系数 * 散射光强度*max(cos(入射角),0)
vec4 pointLight(vec3 normal,vec3 lightLocation, vec4 lightDiffuse){ vec3 vp=normalize(lightLocation‐(vMatrix*vec4(vPosition,1)).xyz); vec3 newTarget=normalize((vMatrix*vec4(normal+vPosition,1)).xyz ‐ (vMatrix*vec4(vPosition,1)).xyz); return lightDiffuse* max(0.0,dot(newTarget,vp)); }
-
镜面光 = 材质反射系数 * 镜面光强度 * max(cos(入射角)^粗糙度 ,0)
void pointLight( //定位光光照计算的方法 in vec3 normal, //法向量 inout vec4 specular, //镜面反射光分量 in vec3 lightLocation, //光源位置 in vec4 lightSpecular //镜面光强度 ){ vec3 normalTarget=aPosition+normal; vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz‐(uMMatrix*vec4(aPosition,1)).xyz; newNormal=normalize(newNormal); vec3 eye= normalize(uCamera‐(uMMatrix*vec4(aPosition,1)).xyz); vec3 vp= normalize(lightLocation‐(uMMatrix*vec4(aPosition,1)).xyz); vp=normalize(vp); vec3 halfVector=normalize(vp+eye); float shininess=50.0; float nDotViewHalfVector=dot(newNormal,halfVector); float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess)); specular=lightSpecular*powerFactor; }
-
定位光
void directionalLight( in vec3 normal, inout vec4 ambient, inout vec4 diffuse, inout vec4 specular, in vec3 lightDirection, in vec4 lightAmbient, in vec4 lightDiffuse, in vec4 lightSpecular ){ ambient=lightAmbient; //环境光 //计算变之后的法向量 vec3 normalTarget=aPosition+normal; vec3 newNormal=(uMMatrix*vec4(normalTarget,1)).xyz‐(uMMatrix*vec4(aPosition,1)).xyz; newNormal=normalize(newNormal); //计算表面点到照相机的向量 vec3 eye= normalize(uCamera‐(uMMatrix*vec4(aPosition,1)).xyz); //规格化定向光方向向量 vec3 vp= normalize(lightDirection); vec3 halfVector=normalize(vp+eye); //求视线与光线的半向量 float shininess=50.0; //粗糙度 ,越小越光滑 ///散射光=材质反射系数 * 散射光强度*max(cos(入射角),0) float nDotViewPosition=max(0.0,dot(newNormal,vp)); //求法向量vp的点积与0的最大值 diffuse=lightDiffuse*nDotViewPosition; //镜面光=材质反射系数 * 镜面光强度 * max(cos(入射角)粗糙度 ,0) float nDotViewHalfVector=dot(newNormal,halfVector); //点积(法线与半向量的点积) float powerFactor=max(0.0,pow(nDotViewHalfVector,shininess));//镜面光反射强度的因子 specular=lightSpecular*powerFactor; }
5.图片处理
算法 | 原理 |
---|---|
灰度 | 1.浮点算法:Gray= R * 0.3+G * 0.59 +B * 0.114 2.整数算法:Gray=( R * 30+G * 59 +B * 11) 3.仅取绿色:Gray=G 4.平均值:Gray=( R+G +B )/3 5.位移算法:Gray=( R * 76+G * 59 +B * 28)>>8 |
冷暖 色调 | 冷色调:增加单一的蓝色分量 暖色调:增加单一的红色和绿色色分量 |
模糊 处理 | 普通模糊:平均 高斯模糊:加权平均 |
放大 | 纹理坐标范围变小 ( s/2.0,t/2.0) |
颠倒 | 1.0-T的坐标 |
扭曲 | 不同半径位置旋转角度不同 (-(r/R)*(r/R)+1.0) |
浮雕 | 当前点的RGB值减去相邻的RGB值然后加上128作为新的RGB值! |
马赛克 | 把图片的一个区域用同一个点的颜色来表示,(大规模降低分辨率,隐藏图像细节) |
对比度增强 | x(1-a)+ya(线性插值) |
膨胀 腐蚀 | 亮的更亮,反之 |
6 OBJ文件和MTL文件
6.1 obj文件
# | 说明 |
---|---|
v --vertex | 顶点坐标 |
vt --vertex texture | 纹理坐标 |
vn --vertex normal | 顶点法向量 |
g --group | 组 |
f --face | 面 1/2/3 分布代表 顶点索引 纹理索引 顶点法向量索引 |
6.2 mtl文件
# | 说明 |
---|---|
Ka /Kd/ Ks | 光照强度 a--环境光 d--散射光 s--镜面光 |
newmtl _5___Default | 材质名 |
illum | 材质光照模型 |
Ns | 材质反射系数 |
bump | 为材质指定凹凸的纹理文件 |
map_KA/map_KS/map_Kd | 为对应光匹配的指定纹理文件 a--环境光 d--散射光 s--镜面光 |
map_d | 为消隐指数指定标量纹理文件 |
refl | 指定一个球体区域,将指定的纹理反射到映射物体上 |
7 混合
7.1 源因子和目标因子
输入片元,当前存储在帧缓存中的像素颜色值和
7.2 混合原理
源颜色(Rs,Gs,Bs,As)
目标颜色(Rd,Gd,Bd,Ad)
设源因子 为(Sr,Sg,Sb,Sa)
设目标因子 为(Dr,Dg,Db,Da)
混合表达式:
混合效果=(Rs * Sr + Rd * Dr , Gs * Sg+Gd * Db , Bs * Sb + Bd * Db,As * Sa + Ad * Da)
7.3 代码
//开启混合
gl.glEnable(GL10.GL_BLEND);
//定义像素算法(源混合因子,目标混合因子)
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_DST_ALPHA);
8 雾
-
数学模型
线性算法:f=max(min((end‐dist)/(end‐start),1.0),1.0) f : 雾化因子(1不浓,0浓) dist: 当前要绘制的片元到照相机的距离 start:特定距离,当前片元距离照相机距离小于start的时候 ,f=1 end: 特定距离,当前片元距离照相机距离大于end的时候 ,f=0 非线性算法:f=1.0‐smoothstep( start, end, dist ) smoothstep函数 如果:dist<=start 返回0.0 如果:dist>=end 返回1.0 如果:start < dist< end 执行0‐1之间的平滑埃尔米特插值 如果:start 》= end 没效果(未定应)
-
代码
float get(){ //计算顶点到照相机的距离 float fogDistance=length( uCamera‐(uMMatrix*vec4(aPosition,1.0)).xyz); const float end=490.0;//雾的结束位置 const float start=350.0;//雾的开始位置 //计算雾因子 float tmp=1.0‐smoothstep(start,end,fogDistance); return tmp; }
9 压缩纹理
(1)图片占用内存公式 内存大小=宽高每一个像素的位数 (2)核心代码
// 读取ZipInputStream 并且创建纹理( ETC1Util.ETC1Texture)
ETC1Util.ETC1Texture t = getNextTexture();
ETC1Util.ETC1Texture tAlpha = getNextTexture();
//加载ETC1纹理
ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D
, 0//纹理层次
, 0//边框大小。通常为0.
, GLES20.GL_RGB//格式使用ETC1纹理压缩,如果不支持。必须gl_rgb。
//要使用的类型如果ETC1纹理压缩不支持。
// 可以gl_unsigned_short_5_6_5,
// 这意味着每个像素的16位或gl_unsigned_byte,这意味着每像素24位。
, GLES20.GL_UNSIGNED_SHORT_5_6_5
, t);//ETC1Util.ETC1Texture
ETC1Util.loadTexture(GLES20.GL_TEXTURE_2D, 0, 0, GLES20.GL_RGB, GLES20
.GL_UNSIGNED_SHORT_5_6_5, tAlpha);
10 FBO
(1)Frame Buffer和Render Buffer (2)核心代码
//创建缓冲帧
GLES20.glGenFramebuffers(1, fFrame, 0);
GLES20.glGenRenderbuffers(1, fRender, 0);
//绑定RenderBuffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, fRender[0]);
//为深度的Render Buffer,并传入大小
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER
, GLES20.GL_DEPTH_COMPONENT16,
mBitmap.getWidth(), mBitmap.getHeight());//渲染缓存图像的像素维度
//创建的渲染缓冲区挂载到帧缓冲区上
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, fRender[0]);
//解绑Render Buffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, 0);
//绑定FrameBuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fFrame[0]);
GLES20.glFramebufferTexture2D(
GLES20.GL_FRAMEBUFFER
, GLES20.GL_COLOR_ATTACHMENT0
, GLES20.GL_TEXTURE_2D
// 附加的纹理对象ID
, fTexture[1]
, 0);
//为FrameBuffer挂载fRender[0]来存储深度
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,//帧缓冲类型的目标
GLES20.GL_DEPTH_ATTACHMENT,// 附着点
GLES20.GL_RENDERBUFFER,// 必须为GL_RENDERBUFFER
fRender[0]); // 渲染缓冲区对象
//设置视口
GLES20.glViewport(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
//绘制 帧缓存里读取一个像素块
draw();
GLES20.glReadPixels(0, 0//定义图像区域左下角点的坐标
//图像的高度和宽度
, mBitmap.getWidth(), mBitmap.getHeight()
//所读象素数据元素的格式
, GLES20.GL_RGBA,
//数据类型
GLES20.GL_UNSIGNED_BYTE,//每个元素的数据类型
mBuffer);//图片
//删除纹理
GLES20.glDeleteTextures(2, fTexture, 0);
//删除Render Buffer
GLES20.glDeleteRenderbuffers(1, fRender, 0);
//删除Frame Buffer
GLES20.glDeleteFramebuffers(1, fFrame, 0);
注意:本文归作者所有,未经作者允许,不得转载