UV坐标,2D贴图,冯氏光照模型和材质

2D贴图的UV坐标

  • 2D贴图一般使用正方形贴图,OpenGL也仅接受正方形的2D贴图。
  • U指横向坐标,V指纵向坐标
    • 2D贴图 的UV坐标范围为:从左到右[0,1],从下到上[0,1]。
    • 2D贴图 的四角分别对应:
      • 左上[0,1],左下[0,0],右上[1,1],右下[1,0]。  

            

  • 在使用UV坐标贴合片元时,先默认2D片元的坐标在OpenGL标准坐标系内,且垂直于z轴。
    • 先仅为[x,y]指定一个UV坐标:[0.5,1],[0,0],[1,0]  > 这里指定使用 [逆时针] 绘制正面
      • 使用 glFrontFace() 可以设置正面的绘制顺序:顺时针 | 逆时针
      • 使用 glCullFace() 可以设置不绘制:背面 | 正面
    • 之后再沿OpenGL左手系x轴旋转片元,得到拥有纵深的图形:

        

    •  它看起来好像扁了一样,实际上也确实是因为旋转后  X,Y,Z坐标发生了变化。
      • 虽然旋转后看起来好像扁了,但在OpenGL标准坐标空间中,它依然是原来那个形状的三角形。
      • 只是此时还未对它使用 [透视变换],无法得到那种看起来更贴合现实的景深感。

 

2D贴图的填充方式

  • 当顶点围城的片元,尚处在 [2D贴图的UV坐标范围] 内时,贴图尚且足够填充片元,但当超出UV坐标范围后呢?
    • 使用 glTexParameteri(
      • GL_TEXTURE_2D,                                  //Flags ,可以是2D贴图,或者3D贴图,
      • GL_TEXTURE_WRAP_S,                       //横向(S),也可以是纵向(T)
      • GL_REPEAT);                                          //填充方式。
    • 可以设置当前 [绑定GL_TEXTURE_2D的贴图] 的填充方式。 

2D贴图的过滤方式

  • 当缩放&旋转  2D片元时,2D片元的材质,可能也会跟着“形变”,此时:
    • 屏幕上原来范围的像素,会发生变化,变化后的颜色,需要计算机运算得出。
    • 过滤方式,也就是计算方式,有:
      • 临近插值:GL_NEAREST
        • 很粗糙的取值办法,取值办法和它的名字一致。
        • 不适合放大时使用,会出现颗粒状,即便你是8bit爱好者,也不建议你这么用,效果是真的不好。
        • 缩小时,可以使用这种办法,毕竟缩小虽然不会引起图像模糊,但粗糙的插值算法会使图像失真。
      • 线性插值:GL_LINEAR
        • maybe它内部就是三线性插值?大概吧,放大时,材质就会模糊,但至少很平滑。
        • 缩小时,不必使用这种插值方法,浪费性能。
    • 使用 glTexParameteri(
      • GL_TEXTURE_2D,                                    // Flags ,可以是2D贴图,或者3D贴图,
      • GL_TEXTURE_MAG_FILTER,                       // [放大] 时的过滤方式,或者缩小时的过滤方式
      • GL_LINEAR);                                             // 过滤方式 FLAGS
    • 可以设置当前 [绑定GL_TEXTURE_2D的贴图] 的过滤方式。 
  • 多级渐远纹理
    • 考虑缩小缩放时,无论使用何种过滤方式,都会引起图像失真(放大缩放仅需准备更加精细的贴图即可),
      • 那不如就为各个级别的缩放级 仔细的单独计算出 属于自己的贴图。
        • 在创建完一个纹理后,调用:glGenerateMipmaps(GL_TEXTURE_2D)
        • 可以为当前绑定  GL_TEXTURE_2D 的贴图创建多级渐远纹理。
        • 创建多级渐远纹理会消耗性能,请尽量对需要更多重复使用的纹理创建,对于那些无伤大雅的细节贴图,可以考虑不使用多级渐远纹理。
      • 再使用线性插值&临近插值,就可以使插值更加精确一些。
        • GL_NEAREST_MIPMAP_NEAREST
          • 使用临近插值进行采样,采样源为最邻近级别
        • GL_LINEAR_MIPMAP_NEAREST
          • 使用线性插值进行采样,采样源为最邻近级别
        • GL_NEAREST_MIPMAP_LINEAR
          • 使用临近插值进行采样,采样源来自:两个 [临近的多级渐远纹理] 的线性插值结果
        • GL_LINEAR_MIPMAP_LINEAR
          • 使用线性插值进行采样,采样源来自:两个 [临近的多级渐远纹理] 的线性插值结果
      • 越向下,越精准,性能消耗越大。
    • 多级渐远纹理仅用于纹理缩小,错误用于纹理放大时,不会产生任何效果,还会返回GL_INVALID_ENUM错误代码。
      • 使用 glTexParameteri(
        • GL_TEXTURE_2D,                                    // Flags ,可以是2D贴图,或者3D贴图,
        • GL_TEXTURE_MIN_FILTER,                         // [缩小] 时的过滤方式,[放大不使用这种过滤方式]
        • GL_LINEAR_MIPMAP_LINEAR);                   // 过滤方式 FLAGS
      • 可以设置当前 [绑定GL_TEXTURE_2D的贴图] 的过滤方式。 

2D贴图的加载

  • 使用stbimage.h可以从贴图文件中加载贴图。
  • 加载代码如下:
  •  1 #define STB_IMAGE_IMPLEMENTATION   //stb_image.h库中提示,如需使用stb_image.h则必须定义此内容
     2 #include<stb_image.h>
     3 
     4 unsigned int TextureFromFile(const char *path, 
     5                              const string &directory, //由main函数提供的directory
     6                              bool gamma)
     7 {
     8     string filename = string(path);
     9     filename = directory + '/' + filename;
    10 
    11     unsigned int textureID;
    12     glGenTextures(1, &textureID);
    13     //申请一个 [textureID],并为其分配存放贴图的内存
    14 
    15     int width, height, nrComponents;
    16     unsigned char *data = stbi_load(filename.c_str(), &width, &height, &nrComponents, 0);
    17     //将文件初始化到文件指针data
    18     if (data)
    19     {
    20         GLenum format;
    21         if (nrComponents == 1)
    22             format = GL_RED;
    23         else if (nrComponents == 3)
    24             format = GL_RGB;
    25         else if (nrComponents == 4)
    26             format = GL_RGBA;
    27         //根据贴图文件的通道数决定接下来 初始化glTexImage2D要用的参数.
    28 
    29         glBindTexture(GL_TEXTURE_2D, textureID);
    30         glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    31         glGenerateMipmap(GL_TEXTURE_2D);
    32 
    33         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);   //设定横向填充方式
    34         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   //设定纵向填充方式
    35         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);   //设置缩放--时的过滤方式
    36         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);   //设置缩放++时的过滤方式
    37 
    38         stbi_image_free(data);
    39         //初始化贴图后,销毁文件指针
    40     }
    41     else
    42     {
    43         //如果data初始化失败,未分配地址则:
    44         std::cout << "Texture failed to load at path: " << path << std::endl;
    45         stbi_image_free(data);
    46         //销毁文件指针
    47     }
    48 
    49     return textureID;
    50 }

     

  • 点击stb_image.h获取 库文件。

光照渲染,冯氏光照模型:

  • 补色原理:
    • 因为所有的颜色,都是不同浓度的RGB基色混合而成的,
      • 所以使用向量  glm::vec3 Color(R,G,B)  来表达任何颜色。
      • R,G,B分别是 [0,1] 的值,用来标识 红绿蓝 的亮度。
    • 用乘法来描述 光线照射片元后,片元的颜色。
      • 设:glm::vec3 LightColor(LR,LG,LB)  是光线颜色。
      • 则:片元颜色 = Color(R,G,B)*LightColor(LR,LG,LB) = glm::vec3(R*LR,G*LG,B*LB)
    • 用加法来描述不同光线的组合:
      • 设:glm::vec3 LightColor()  是光线颜色。
      • subLightColor_1(LR_1,LG_1,LB_1) + subLightColor_2(LR_2,LG_2,LB_2) = glm::vec3 LightColor(LR_1+LR_2,LG_1+LG_2,LB_1+LB_2)
  • 光照渲染,其实就是通过 [补色原理] 在片元上 [叠加着色] 来模拟光照效果。
  • 法向量及其缩放:
    • 法向量在OpenGL光照中充当 法线的作用
      • 法向量(glm::vec3) 一般在模型文件中都存在此值,可以在[Assimp数据结构]中解析到。
        • Assimp是一个用于解析模型数据的库,在nuget包管理器中可以找得到。
      • 法向量一般通过VAO传送到shader。
    • 对模型的不等比缩放,可能会引起顶点法向量的形变,因此要在

 

  • 冯氏光照模型:
    • 通过混合 [三种光照渲染],来模拟现实世界光照在片元上的效果,如图:

      

    • 三种光照渲染包括:
      • 漫反射光照,高光光照,环境光照。
    • 漫反射光照:
      • 模拟的情况:
        • 物体在被光照时,首先会被整体点亮。
        • 存在折射率,因此存在损耗,最终从不同角度反射回的光量便不同。
    • 高光光照:
      • 模拟的情况:
        • 不管何种物体的表面,都可以反射回光源的情况。
        • 根据物体的光滑程度不同,最终会影响高光的散射度。
          • 更光滑的,也就更闪闪发光,它的散射度也就越小,反之越大。
    • 环境光照:
      • 模拟的情况:
        • 在没有任何光源的时候,人眼依旧可以看得见物体,环境光照模拟的就是这种情况。

 

  • 点光源的冯氏光照样例:
    • 因为光照渲染可以直接在Shader中完成,因此此处展示两个subShader:
    • vertexShader:
    •  1 #version 450 core
       2 layout (location = 0) in vec3 aPos;             //原始顶点位置数据         
       3 layout (location = 1) in vec3 aNormal;          //原始顶点法向量
       4 layout (location = 2) in vec2 aTexCoords;       //顶点UV坐标
       5 
       6 out vec3 FragPos;       //model变换之后的 > 顶点位置数据
       7 out vec3 Normal;        //归一化之后的顶点法向量
       8 out vec2 TexCoords;     //UV坐标
       9 
      10 uniform mat4 model;        //model变换矩阵
      11 uniform mat4 view;         //view(camera)变换矩阵
      12 uniform mat4 projection;   //透视裁剪变换矩阵
      13 
      14 void main()
      15 {
      16     //计算model变换后的顶点位置:
      17     FragPos = vec3(model * vec4(aPos, 1.0));
      18 
      19     //法向量归一化,防止不等比缩放影响法向量:
      20     Normal = mat3(transpose(inverse(model))) * aNormal; 
      21 
      22     //数据本地化:
      23     TexCoords = aTexCoords;
      24     
      25     //顶点变换,以及数据交付:
      26     gl_Position = projection * view * vec4(FragPos, 1.0);
      27 }
    • fragmentShader:
    •  1 #version 450 core
       2 
       3 //顶点按组绘制,每组构成一个片元,shader每执行一次绘制一个片元(一组顶点)
       4 out vec4 FragColor;
       5 
       6 //材质---------------------------
       7 //意思就是片元表面的渲染属性。
       8 struct Material {
       9     sampler2D diffuse;
      10     sampler2D specular;    
      11     float shininess;
      12 }; 
      13 //此处仅包括片元的贴图和高光反射级别。
      14 
      15 //光线---------------------------
      16 //光线属性,渲染光照时需要使用。
      17 struct Light {
      18     vec3 position;
      19 
      20     vec3 ambient;
      21     vec3 diffuse;
      22     vec3 specular;
      23 };
      24 //此处的光线属性包括: 光源位置,三种光照强度。
      25 
      26 /*-------------以上是glsl自定义结构体类型,glsl是类c语言,结构体也和c一致------------*/
      27 
      28 
      29 //来自之前的subShader的值。
      30 in vec3 FragPos;  
      31 in vec3 Normal;  
      32 in vec2 TexCoords;
      33 
      34 //摄像机在世界空间坐标系中的坐标,即【OpenGL标准坐标系原点】在世界空间坐标系中的位置  
      35 uniform vec3 viewPos;
      36 
      37 //外部虽然没有 Material&Light 的定义,
      38 //但可以根据实例名 material.StructuralComponent向内传值
      39 uniform Material material;
      40 uniform Light light;
      41 
      42 void main()
      43 {
      44 
      45 //渲染前置数据准备:
      46     //法向量归一化(标准化,不归一化方向可能会不同)
      47     vec3 norm = normalize(Normal);  
      48     //更新光照路径,归一化(不归一化方向可能会不同)
      49     vec3 lightDir = normalize(light.position - FragPos);
      50     //更新视角路径(渲染specular光照时会使用),归一化(不归一化方向可能会不同)
      51     vec3 viewDir = normalize(viewPos - FragPos);
      52     //更新反射路径(渲染specular光照时会使用),归一化(不归一化方向可能会不同)
      53     vec3 reflectDir = reflect(-lightDir, norm);
      54 
      55 // 计算出光照后的片元渲染颜色
      56 // -----------------------------------------
      57     // ambient光照----------------------------------------------------------
      58     // ambient作用于 diffuseTexture,因此使用:material.diffuse,
      59     vec3 ambient = light.ambient * texture(material.diffuse, TexCoords).rgb;
      60 
      61     // diffuse光照----------------------------------------------------------
      62     // 计算此片元 漫反射强度
      63         //根据光源位置不同,折射现象对反射强度的影响也不同 > 此处仅考虑这种情况
      64         //根据折射率(来自材质)不同,漫反射强度也会有所衰减 > 此处不考录这种情况,默认所有材质的折射率都是最小值
      65     float diff = max(dot(norm, lightDir), 0.0);
      66     // diffuse作用于 diffuseTexture,因此使用:material.diffuse,
      67     vec3 diffuse = light.diffuse * diff * texture(material.diffuse, TexCoords).rgb;  
      68     
      69     // specular光照---------------------------------------------------------
      70     // 计算此片元 高光反射强度
      71         //根据视线位置不同,以及反射路径,就会影响高光反射在不同位置的强度,进而形成“光晕”效果
      72         //不同物体的散射程度不同,越光滑,散射越低,同时越亮  
      73     float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
      74     // specular作用于 specularTexture,因此使用:material.specular,
      75     vec3 specular = light.specular * spec * texture(material.specular, TexCoords).rgb;  
      76         
      77     //组合结果:
      78     vec3 result = ambient + diffuse + specular;
      79     FragColor = vec4(result, 1.0);
      80 } 
  •  材质:
    • 材质你可以认为是:来自渲染目标的,影响渲染的因素。
    • 以上样例中的材质为:
      • struct Material {
        • sampler2D diffuse
        • sampler2D specular
        • float shininess;
      • };
posted @ 2021-09-28 23:31  SupersLWF  阅读(540)  评论(0)    收藏  举报