OpenGL知识点总结

作者:じ☆ve不哭

发布时间:2023-08-21T09:41:49

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 支持版本

androidversion

1.5 OpenGL绘制流程

process

  1. 从OpenGL的几何图元中设置数据,用于构建形状
  2. 用不同的着色器对输入的图元数据执行计算操作,判断位置,颜色以及其他渲染属性
  3. 输入图元的数学描述 转换为与屏幕位置对应的像素片元,也称光栅化
  4. 针对光栅化过程产生的每个片元,执行 片元着色器,从而决定这个片元的最终颜色和位置
  5. 如果有必要 可以对片元执行一些额外操作(判断片元对应的对象是否可见,或者将片元的颜色与当前屏幕位置的颜色进行融合。)

2.GLSL

2.1 着色器介绍

  • 顶点着色器------- 将虚拟空间三维坐标 映射到 屏幕显示二维坐标 +Z-buffer深度信息

    模型如下:

    vertex

  • 片元着色器 --------计算每个像素的颜色和其它属性 (如光照值,贴图,阴影)

    模型如下:

fragment

2.2 七大数据类型

  1. 标量----基本数据类型

    • int 32位
    • float 32位
    • bool 8位
  2. 向量----常用于表示颜色,纹理,坐标

    base

  3. 矩阵----常用于位移,旋转,缩放操作

    • 类型 mat2 mat3 mat4
    • 填充顺序从下向下,从左往右
  4. 采样器----纹理采样 (用在片元着色器,在宿主(java)中初始化)

    • 3类采样器 sampler2D sampler3D samplerCube(立体贴图,需要手动开启功能)
  5. 结构体----简化运算

    struct info {
    			vec3 a;  
    			vec2 b;
                 vec3 c
                 }
    
  6. 数组----float f[]

  7. 空类型 ----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 映射原理

  1. ​ 为顶点指定纹理坐标
  2. ​ 通过纹理坐标确认纹理区域
  3. ​ 将选定区域根据纹理坐标映射到图元上

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 雾

  1. 数学模型

    线性算法: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  没效果(未定应) 
    
  2. 代码

    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);