LearnOpenGL-高级光照-1.Blinn-Phong

news2024/11/18 5:58:37

本人初学者,文中定有代码、术语等错误,欢迎指正

文章目录

  • 高级光照
    • Phong光照的缺点
    • Blinn-Phong
      • 介绍
      • 例子
  • GLSL中遇到的BUG

高级光照

Phong光照的缺点

  • 造成Phong光照缺点的两个条件

    • 当物体的高光反光度(shiness)比较

      • 什么是高光的反光度

        回顾LearnOpenGL-光照-2.基础光照

        在片段着色器计算镜面光照分量

        float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32);// spec = 光源对当前片段的镜面光影响
        vec3 specular = specularStrength * spec * lightColor;// specular = 镜面光照分量
        

        第一行代码:float spec=…,其中32就是片段的高光反光度

      • shiness小到什么程度

        小于1以下(下面有例子:代值计算shiness比较小时对spec的影响)

    • 视线与反射光线向量的夹角大于90度

      左图小于90度,右图大于90度

  • 满足上一点的两个条件,会引起问题

    • 图示

      请添加图片描述

    • 文字描述

      如红色箭头所指,在镜面高光区域的边缘出现了一道很明显的断层

  • 两个条件转换为具体值计算如何影响spec的(大概率有误,请指正)

    • 测试前需了解

      cos:在0-90度是1至0,在90度-180度是0至-1

    • 测试pow(a,b)值

      a = dot(viewDir, reflectDir)

      b = shiness

      // 用例一
      for (float i = 0; i <= 0.1; i += 0.01) {
          float spec = pow(i, 0.2);// shiness:0.2 < 1
          cout << "pow("<< i <<", 0.2):" << spec << endl;
      }
      cout << "---" << endl;
      // 用例二
      for (float i = 2; i <= 2.1; i += 0.01) {
          float spec = pow(i, 0.2);// shiness:0.2 < 1
          cout << "pow(" << i << ", 0.2):" << spec << endl;
      }
      cout << "---" << endl;
      // 用例三
      float spec = pow(-0.1, 0.2);// shiness:0.2 < 1
      cout << "pow(-0.1, 0.2):" << spec << endl;
      cout << "---" << endl;
      // 用例四
      spec = pow(-2, 2);			// shiness:2 > 1
      cout << "pow(-2, 2):" << spec << endl;
      

    看输出可知

    1. a^b,a=0.01<1,b=0.2<1,pow(a,b)后于a

    2. a^b,a=2.01>1,b=0.2<1,pow(a,b)后于a

    3. a^b,a<0,b=0.2<1,pow(a,b)后非法数字

    4. a^b,a<0,b=2>1 ,pow(a,b)后合法数字

  • 再回到片段着色器中计算spec

    spec = pow(max(dot(viewDir, reflectDir), 0.0), 0.2);// 光源对当前片段的镜面光影响
    vec3 specular = specularStrength * spec * lightColor;// 镜面光照分量
    
    • 当视线与反射光线向量的夹角大于90度,比如夹角是:120

      1. dot(viewDir, reflectDir) = cos(120) = -0.5

      2. max(-0.5, 0.0) = 0 (注意:max(-0.5 , 0 ) 取最大的为0)

      3. 从而spec = pow(a=0, b=0.2) = 0

      4. 则specular = 0,根本没有镜面光照分量,即:这个片段没有镜面高光,不会造成此片段的颜色太亮

      个人认为:镜面高光区域的边缘出现了一道很明显的断层,此时视线与反射光线向量的夹角并不大于90度(大概率我错了,求大佬指正)

    • 但是图中边缘区域确实有镜面高光断层

      则只有可能是视线与反射光线向量的夹角小于90度,并且接近90度,从而cos角度为很小的值,比如夹角是:87.708

      1. dot(viewDir, reflectDir) = cos(87.708) 约等于0.04

      2. max(dot(viewDir, reflectDir), 0.0) = 0.04

      3. spec = pow(0.04, 0.2) = 0.525306 (对应上述代值计算的 用例一)

      4. specular = specularStrength * 0.525306 * vec3(1)

      镜面光照分量 = specularStrength * spec * 光照颜色白色1,而0.525306约是1/2,1是白色,1/2是一半的白色,所以边缘区域的光高亮,出现断层

Blinn-Phong

介绍

  • 目的

    解决上述Phong提到的问题(在镜面高光区域的边缘出现了一道很明显的断层

  • 解决方式

    不再计算反射向量与观察向量的点积。

    而是用半程向量(Halfway Vector)与法线向量的点积。

  • 什么是半程向量

    光线与视线夹角一半方向上的一个单位向量

  • 图示

  • 几个要点

    • 当半程向量与法线向量越接近时,镜面光分量就越大
    • 不论观察者向哪个方向看,半程向量与表面法线之间的夹角都不会超过90度(除非光源在表面以下)
  • 如何计算半程向量

    只需要将光线的方向向量和观察向量加到一起,并将结果正规化(Normalize)

    // 半程向量
    vec3 halfwayDir = normalize(lightDir + viewDir);
    

例子

  • 代码

    glsl

    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec3 aNormal;
    layout (location = 2) in vec2 aTexCoords;
    out VS_OUT{// 4.8节讲的接口块
        vec3 FragPos;
        vec3 Normal;
        vec2 TexCoords;
    }vs_out;
    
    uniform mat4 projection;
    uniform mat4 view;
    uniform mat4 model;
    
    void main()
    {
        // 虽然没乘model矩阵到世界空间,但model是单位矩阵,可以认为已经在世界空间了
        vs_out.FragPos = aPos;
        vs_out.Normal = aNormal;
        vs_out.TexCoords = aTexCoords;
        gl_Position = projection * view  * vec4(aPos, 1.0);
    }
    
    #version 330 core
    out vec4 FragColor;
    
    in VS_OUT{
        vec3 FragPos;
        vec3 Normal;
        vec2 TexCoords;
    }fs_in;
    
    uniform sampler2D floorTexture;
    uniform vec3 lightPos;
    uniform vec3 viewPos;
    uniform bool blinn;
    
    void main()
    {
        // 采样纹理作为光照颜色
        vec3 color = texture(floorTexture, fs_in.TexCoords).rgb;
        // 环境光
        float ambientStrength = 0.05;
        vec3 ambient = ambientStrength * color;
        // 漫反射
        vec3 lightDir = normalize(lightPos - fs_in.FragPos);
        vec3 normal = normalize(fs_in.Normal);
        float diff = max(dot(normal, lightDir), 0.0);
        vec3 diffuse = diff * color;
        // 环境光照
        vec3 viewDir = normalize(viewPos - fs_in.FragPos); // 是观察者方向,不是观察者看向的方向
        float spec = 0.0;
        if(blinn){
            
            // blinn-pong:半程方向向量与法线方向向量的点积
            // 半程向量///
            vec3 halfwayDir = normalize(lightDir + viewDir);
            spec = pow(max(dot(normal, halfwayDir), 0.0), 1);
        }
        else
        {
            // pong:观察者方向向量与反射方向向量的点积
            vec3 reflectDir = reflect(-lightDir, normal);
            spec = pow(max(dot(viewDir, reflectDir), 0.0), 1);
        }
        float specularStrength = 0.3;
        vec3 specular = specularStrength * spec * vec3(1);// 不像漫反射需要乘以纹理颜色,而是乘以1,表示镜面光的颜色为白色
        FragColor = vec4(ambient + diffuse + specular, 1.0);
    }
    

    cpp

    float planeVertices[] = {
        // positions            // normals         // texcoords
        10.0f, -0.5f,  10.0f,  0.0f, 1.0f, 0.0f,  10.0f,  0.0f,
    	.......
    };
    // first, configure the cube's VAO (and VBO)
    unsigned int planeVBO, planeVAO;
    glGenVertexArrays(1, &planeVAO);
    glBindVertexArray(planeVAO);
    glGenBuffers(1, &planeVBO);
    glBindBuffer(GL_ARRAY_BUFFER, planeVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(planeVertices), planeVertices, GL_STATIC_DRAW);
    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);
    
    unsigned int floorTexture = loadTexture("assest/textures/wood.png");
    
    lightingShader.use();
    lightingShader.setInt("texture1", 0);
    
    // render loop
    // -----------
    while (!glfwWindowShouldClose(window))
    {
        ......
        lightingShader.use();
        // view/projection transformations
        glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);
        glm::mat4 view = camera.GetViewMatrix();
        lightingShader.setMat4("projection", projection);
        lightingShader.setMat4("view", view);
        lightingShader.setMat4("model", glm::mat4(1.0f));
    
        lightingShader.setInt("blinn", blinn);// 控制是否开启blinn-phong
        lightingShader.setVec3("viewPos", camera.Position);
        lightingShader.setVec3("lightPos", lightPos);
    
        // render the cube
        glBindVertexArray(planeVAO);
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, floorTexture);
        glDrawArrays(GL_TRIANGLES, 0, 36);
        .......
    void processInput(GLFWwindow* window){
        .......
        if (glfwGetKey(window, GLFW_KEY_V) == GLFW_PRESS) {
            blinn = false;
        }
        if (glfwGetKey(window, GLFW_KEY_B) == GLFW_PRESS) {
            blinn = true;
        }
    }
    
  • 效果

    blinn-phong

    请添加图片描述

    phong

    请添加图片描述

  • 原文与Phong对比

GLSL中遇到的BUG

  • Bug如图

    明明在if else 外部定义了spec变量,而glsl却报未定义spec变量,百思不得琦姐。

  • 解决方法

    不知道为什么,空了一行就能运行了

  • 原因找到

    由于29行的末尾的中文注释有个**\**,从而引起的问题,可能会引起转义成字符什么问题。

    去掉这个**\**,即时不空行,也能正确运行了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/634100.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试员将迎来春天

这几年因为疫情、经济寒冬&#xff0c;导致IT从业者工作地很不容易。 而IT从业者中的测试员&#xff0c;这两年过得尤为艰难。大家都知道&#xff0c;不直接生产的测试员&#xff0c;在企业降本增效的口号下一定是首当其冲被优化掉的成本。所以&#xff0c;小厂倒了一大批&…

沐风晓月个人博客折腾记: 从零开始加上漂亮的前端模板,轻松提升博客品味

前言 在个人博客折腾记的专栏里&#xff0c;我们已经安装好了wordpress&#xff0c;能用但看上去不够好看&#xff1a; 我们希望让前端模板好看一点&#xff0c;如果你有好的前端模板推荐&#xff0c;可以评论区留言哦。 如果你还没有搭建wordpress 可以参考&#xff1a; 利…

C++开发—远程控制

C开发—远程控制 一&#xff0c;准备二&#xff0c;安装版本控制工具1&#xff0c;安装gitforwindows2&#xff0c;安装乌龟git1&#xff0c;安装乌龟git应用2&#xff0c;安装乌龟git对应的语言包 3&#xff0c;设置Visual Studio的git插件4&#xff0c;创建git项目 三&#x…

React Hook入门小案例 在函数式组件中使用state响应式数据

Hook是react 16.8 新增的特性 是希望在不编写 class的情况下 去操作state和其他react特性 Hook的话 就不建议大家使用class的形式了 当然也可以用 这个他只是不推荐 我们还是先创建一个普通的react项目 我们之前写一个react组件可以这样写 import React from "react&qu…

Java学习笔记(视频:韩顺平老师)2.0

如果你喜欢这篇文章的话&#xff0c;请给作者点赞哟&#xff0c;你的支持是我不断前进的动力。 因为作者能力水平有限&#xff0c;欢迎各位大佬指导。 变量 基本数据类型⭐️ 数值型 基本数据类型转化 自动类型转换 强制类型转换 基本数据类型和String类型转换 变量 变量…

Vulnhub靶机渗透:MY FILE SERVER: 1

MY FILE SERVER: 1 nmap扫描端口扫描服务扫描漏洞扫描选择渗透方向 21/2121 ftp445 samba2049/20048 nfs80 http目录爆破 获得立足点提权4061140847 获取flag 靶机链接: https://www.vulnhub.com/entry/my-file-server-1,432/ 靶机IP&#xff1a;192.168.54.33 kali IP&#x…

人工智能轨道交通行业周刊-第48期(2023.6.5-6.11)

本期关键词&#xff1a;铁路测绘、动车组限速、铁路四电、智源大会、苹果AR眼镜、AIGC商业落地 1 整理涉及公众号名单 1.1 行业类 RT轨道交通人民铁道世界轨道交通资讯网铁路信号技术交流北京铁路轨道交通网上榜铁路视点ITS World轨道交通联盟VSTR铁路与城市轨道交通RailMet…

如何通过绩效考核对互联网人精准打击条条致命?

在“经济形势就业压力”的双重打击下&#xff0c;打工人变得越来越温顺。曾经闹着要整顿职场的大多年轻人&#xff0c;也从年少轻狂逐步走向少年老成&#xff0c;突然少了许多“XX后整顿职场”这样的声音。在严峻的复杂形势下&#xff0c;大多公司为了降本增效&#xff0c;殚精…

2017~2018学年《信息安全》考试试题(A3卷)

北京信息科技大学 2017 ~2018 学年第一学期 《信息安全》考试试题 (A3 卷) 课程所在学院&#xff1a;计算机学院 适用专业班级&#xff1a; - 考试形式&#xff1a;闭卷 一、单选题(本题满分 20 分&#xff0c;共含 10 道小题&#xff0c;每小题 2 分) Wanncry 勒索攻击通过加…

LLM下的讨论230611

三、我们能研究什么&#xff1f; 在大模型时代&#xff0c;可以考虑深挖的方向&#xff0c;供大家参考&#xff1a; 3.1 Retrieval augmented in-context learningGPTs完成了NLP范式的更新迭代&#xff1a;从传统的有监督学习&#xff08;Supervised Learning&#xff09;转变…

有趣的图(一)(55)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 咱们今天的内容比较抽象&#xff0c;也比较有趣。 这里的图是指计算机中的图&#xff0c;确切地说&#xff0c;是…

Debian 12 x86_64 OVF (sysin) - 虚拟机自动化模板

Debian 12 x86_64 OVF (sysin) - VMware 虚拟机模板 请访问原文链接&#xff1a;https://sysin.org/blog/debian-12-ovf/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org Debian GNU/Linux 12 (bookworm) (Linux debian 6.1.0-…

面试20k的测试工程师什么水平?知彼知己百战不殆...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 面试软件测试你需…

一文看懂python如何执行cmd命令

概要 “ 在进行Python编程时&#xff0c;经常需要使用到操作系统的命令行&#xff0c;这就要求我们学会如何使用Python执行cmd命令。” Python执行cmd命令的几种方法 Python是一种强大而灵活的编程语言&#xff0c;它可以很方便地执行系统命令&#xff0c;与操作系统进行交互。…

软件测试人员灵魂三问

可有过高光时刻&#xff1f;职业立足点是什么&#xff1f;前路在何方&#xff1f; 没有光高时刻的职业&#xff0c;不值得留恋 根据马斯洛需求层次理论&#xff0c;当人们温足饭饱后&#xff0c;还需要尊重和自我实现。 同样&#xff0c;作为测试员&#xff0c;工作不仅仅是…

I2C通信协议,最简单的总线通信

串口通信只能在两个设备之间进行&#xff0c;如果是四组串口通信&#xff0c;那每个设备都需要三组串口&#xff0c;其线路连接相当繁琐&#xff08;如下图&#xff09;。 为了解决这个痛点&#xff0c;人们设计了一种总线通信&#xff0c;总线通信有很多种协议&#xff08;如…

记一次gstreamer解码存图绿线问题排查

背景 业务需求需要将某些解码后的视频帧保存为图片&#xff0c;大部分情况下图片都是正常的&#xff0c;更换了某些视频流后&#xff0c;在保存的图片顶部就会出现一条绿线&#xff0c;现记录下解决过程。 部分代码如下 解码回调如下&#xff0c;完整代码可参考之前的文章G…

JVM零基础到高级实战之内存区域分布与概述

JVM零基础到高级实战之内存区域分布与概述 JVM零基础到高级实战之内存区域分布与概述 文章目录 JVM零基础到高级实战之内存区域分布与概述前言Java语言为甚么优势巨大&#xff1f;总结 前言 JVM零基础到高级实战之内存区域分布与概述 Java语言为甚么优势巨大&#xff1f; 一处…

FMCW 雷达室内多目标人员MATLAB仿真

分享一则代码&#xff0c;主要用于FMCW雷达室内多目标MATLAB仿真&#xff0c;涉及到的内容和算法模块有如下&#xff1a; 1、目标参数设置 2、雷达参数设置 3、目标运动状态设置 4、雷达信号建模&#xff08;IQ信号&#xff09; 5、雷达近场收发几何位置偏差校正 6、距离维FFT…

速刷剑指offer

链接&#xff1a;No5、 用两个栈来实现一个队列 | 阿秀的学习笔记 第五题跳过。栈和队列等着代码随想录二刷补上。 JZ11 旋转数组的最小数字 链接&#xff1a;旋转数组的最小数字_牛客题霸_牛客网 代码&#xff1a; 这个二分法是左闭右开的&#xff0c;就真的不好理解。 class …