LearnOpenGL-光照-4.光照贴图

news2024/11/14 5:27:02

本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正

我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject

文章目录

  • 光照贴图
  • 漫反射贴图
    • 例子1
  • 镜面光贴图
    • 例子2 采样镜面光贴图
  • 小结
    • 什么是光照贴图
    • 光照贴图如何影响颜色

光照贴图

  • 上一节手动设置材质的缺点
    • 不能对一个物体的视觉输出提供足够多的灵活性。
    • 将整个物体的材质定义为一个整体,但现实世界中的物体通常并不只包含有一种材质,而是由多种材质所组成

所以我们需要拓展之前的系统,引入漫反射镜面光贴图(Map)。这允许我们对物体的漫反射分量(以及间接地对环境光分量,它们几乎总是一样的)和镜面光分量有着更精确的控制。

漫反射贴图

  • 纹理作用

    能够让我们根据片段在物体上的位置来获取颜色值,让我们能够逐片段索引其独立的颜色值

  • 纹理变为漫反射贴图

    在光照场景中,纹理通常叫做一个漫反射贴图(Diffuse Map)(3D艺术家通常都这么叫它),它是一个表现了物体所有的漫反射颜色的纹理图像。

例子1

glsl

#version 330 core
out vec4 FragColor;

in vec3 Normal;
in vec3 FragPos;
in vec2 TexCoords;// 纹理坐标

uniform vec3 viewPos;

struct Material {
    sampler2D diffuse;	// 纹理单元
    vec3 specular;		// 镜面光照颜色分量依旧是手动设置
    float shininess;
}; 
uniform Material material;

// 光照强度
struct Light {
    vec3 position;

    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};

uniform Light light;
void main()
{
    // 环境光照分量
    float ambientStrength = 0.1;
    // 从漫反射纹理读取颜色分量
    vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords));

    // 漫反射光照分量
    vec3 norm = normalize(Normal);
    vec3 lightDir = normalize(light.position - FragPos);
    float diff = max(dot(norm, lightDir), 0.0);                              // 得到光源对当前片段实际的漫反射影响
    vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));// 从漫反射纹理读取颜色分量

    // 镜面光照分量
    float specularStrength = 0.5;
    vec3 viewDir = normalize(viewPos - FragPos);                            // 是观察者方向,不是观察者看向的方向
    vec3 reflectDir = reflect(-lightDir, norm);

    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 光源对当前片段的镜面光影响
    vec3 specular = light.specular * (spec * material.specular);            // 手动设置的镜面光照颜色分量

    vec3 result = (ambient + diffuse + specular) ;
    
    FragColor = vec4(result, 1.0);
}
  • 可见

    依旧是冯氏光照模型

    • 镜面光照的颜色分量是手动设置的

    • 而漫反射光照的颜色分量是读取纹理

      vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));

      漫反射光照分量 = 光源漫反射颜色分量 * 对当前片段采样漫反射纹理颜色 * 光源对片段的漫反射影响

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;

uniform mat4 view;
uniform mat4 projection;
uniform mat4 model;

out vec3 FragPos;  
out vec3 Normal;
out vec2 TexCoords;
void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    FragPos = vec3(model * vec4(aPos, 1.0));
    Normal = aNormal;
    TexCoords = aTexCoords;
}

cpp

要更新顶点数据(坐标、法线、纹理),要重新设置顶点属性指针,要加载纹理

 // 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, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);

// load and create a texture 
// -------------------------
unsigned int texture1 = loadTexture("assest/textures/container2.png");
// 纹理加载出过错,由于图片是rgba格式,没有自动读取格式,而是硬编码设置了rgb,所以导致错误

// 设置使用的纹理单元
lightingShader.use();
lightingShader.setInt("material.diffuse", 0);

// 绘制前绑定漫反射贴图-出过错,放在绘制光源cube那里
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);

// render the cube
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);

// 根据图片的通道不同,设置加载图像的参数
unsigned int loadTexture(char const* path)
{
    unsigned int textureID;
    glGenTextures(1, &textureID);

    int width, height, nrComponents;
    unsigned char* data = stbi_load(path, &width, &height, &nrComponents, 0);
    if (data)
    {
        GLenum format;
        if (nrComponents == 1)
            format = GL_RED;
        else if (nrComponents == 3)
            format = GL_RGB;
        else if (nrComponents == 4)
            format = GL_RGBA;

        glBindTexture(GL_TEXTURE_2D, textureID);
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
        glGenerateMipmap(GL_TEXTURE_2D);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        stbi_image_free(data);
    }
    else
    {
        std::cout << "Texture failed to load at path: " << path << std::endl;
        stbi_image_free(data);
    }
    return textureID;
}

materials_diffuse_map

镜面光贴图

  • 例子1效果的不足

    如例子1的效果,木头不应该这么强的镜面光。

  • 不足原因

    因为我们手动设置的镜面光强度,适用于整个物体,整个物体的镜面光颜色分量都一样

  • 需要镜面光贴图解决

    引入镜面光贴图,将木头部分的颜色为黑色,glsl读取时会变成0,即:没有镜面光

    而四周金属部分保持原样,glsl读取时会大于0,即:具有镜面光

例子2 采样镜面光贴图

相比例子1

  • cpp

    增加了一个纹理,并设置它的纹理单元

  • glsl

    镜面光不再是手动指定整个物体都是同一个颜色分量,而是读取材质的颜色作为颜色分量

    #version 330 core
    out vec4 FragColor;
    
    in vec3 Normal;
    in vec3 FragPos;
    in vec2 TexCoords;// 纹理坐标
    
    uniform vec3 viewPos;
    
    struct Material {
        sampler2D diffuse;// 纹理单元
        //vec3 specular;    // 镜面光照颜色分量依旧是手动设置
        sampler2D specular;//  镜面光照颜色分量从纹理采样
        float shininess;
    }; 
    uniform Material material;
    
    // 光照强度
    struct Light {
        vec3 position;
    
        vec3 ambient;
        vec3 diffuse;
        vec3 specular;
    };
    
    uniform Light light;
    void main()
    {
        // 环境光照分量
        float ambientStrength = 0.1;
        // 从漫反射纹理读取颜色分量
        vec3 ambient = light.ambient * vec3(texture(material.diffuse, TexCoords)); 
    
        // 漫反射光照分量
        vec3 norm = normalize(Normal);
        vec3 lightDir = normalize(light.position - FragPos);
        float diff = max(dot(norm, lightDir), 0.0);                 // 得到光源对当前片段实际的漫反射影响
        vec3 diffuse = light.diffuse * diff * vec3(texture(material.diffuse, TexCoords));// 从漫反射纹理读取颜色分量
    
        // 镜面光照分量
        float specularStrength = 0.5;
        vec3 viewDir = normalize(viewPos - FragPos); // 是观察者方向,不是观察者看向的方向
        vec3 reflectDir = reflect(-lightDir, norm);
    
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);// 光源对当前片段的镜面光影响
        // vec3 specular = light.specular * (spec * material.specular); // 改变在这里
        // 采样镜面光纹理颜色作为镜面光照颜色分量
        vec3 specular = light.specular * spec * vec3(texture(material.specular, TexCoords)); 
    
        vec3 result = (ambient + diffuse + specular) ;
        
        FragColor = vec4(result, 1.0);
    }
    
  • 效果

    请添加图片描述

小结

什么是光照贴图

  • 上节

    lightingShader.setVec3("material.ambient", 1.0f, 0.5f, 0.31f);
    lightingShader.setVec3("material.diffuse", 1.0f, 0.5f, 0.31f);
    lightingShader.setVec3("material.specular", 0.5f, 0.5f, 0.5f);
    lightingShader.setFloat("material.shininess", 32.0f);
    

    手动定义了物体的材质,即设置了环境光、漫反射光、镜面光照颜色分量

  • 而这节用光照贴图

    代替手动定义材质,从光照贴图中读取三种光照的颜色分量。

    光照贴图等同纹理,只不过在光照场景下,纹理被称为光照贴图。

  • 光照贴图包含

    • 漫反射贴图
    • 镜面光贴图

光照贴图如何影响颜色

  • 漫反射贴图

    根据各个片段的uv读取漫反射贴图上的颜色值,然后作为漫反射、环境光照颜色分量,乘以2.2节光源对片段的漫反射影响再乘以光源颜色分量。

  • 镜面光贴图

    根据各个片段的uv读取镜面光贴图上的颜色值,然后作为镜面光颜色分量,乘以2.2节光源对片段的镜面光影响再乘以光源颜色分量。

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

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

相关文章

开源一个通用的 HTTP 请求前端组件

像 Postman 这样可视化的 HTTP 请求工具是调试 API 不可或缺的利器。Postman 虽好但也越来越重&#xff0c;而且如果要整合到其他工具中&#xff0c;显然 Postman 又不是一个可行的方案。于是我想打造一个简单的前端组件&#xff08;widget&#xff09;&#xff0c;它是一个标准…

天池 DeepRec CTR 模型性能优化大赛 - 夺冠技术分享

作者&#xff1a;niceperf 团队 (李扬, 郭琳) 大家好&#xff0c;我们是 niceperf 团队&#xff0c;在天池 DeepRec CTR 模型性能优化大赛中&#xff0c;很荣幸取得了冠军的成绩 (Top 1/3802)。这篇文章复盘一下我们的参赛经验&#xff0c;希望对大家有所启发。 1.背景介绍 …

KDZD5035系列电缆试验油杯

一、概述 武汉凯迪正大总结十多年的局放试验经验&#xff0c;开发生产了KDZD5035系列电缆试验油杯终端&#xff0c;具有使用方便&#xff0c;性能可靠&#xff0c;本身局放量小等优点&#xff0c;与早期落地式油杯相比&#xff0c;可为用户节约大量的试验成本。 KDZD5520交流…

单片机学习笔记之点阵(8x8)

心血来潮&#xff0c;想捡一下丢了很久的单片机&#xff0c;纪录一下单片机学习简单的点阵显示&#xff0c;及踩到的䟘&#xff0c;找到吃灰很久的普中科技开发板&#xff08;非广告&#xff0c;为毕设学习买的&#xff09;。 1. 使用工具 使用开发板&#xff1a; 普中科技开发…

Hive---自定义函数

Hive自定义函数 文章目录Hive自定义函数定义自定义函数步骤创建一个Maven工程&#xff0c;导入依赖创建自定义函数类在 hive 的命令行窗口创建函数创建临时函数创建永久函数UDF打成 jar 包上传到服务器/opt/soft/hive312/lib/目录下将 jar 包添加到 hive 的 classpath建临时函数…

python数据类型与数据结构

目录 一、数据类型 1.1变量与常量 1.1.1变量 1.1.2常量 1.2字符串类型 1.3整数与浮点数 1.4List列表 1.5 元组tuple 1.6字典dict 二、字符串格式化 三、数据输入和类型转换 四、简单列表习题练习 一、数据类型 变量类型&#xff1a; 整数int&#xff08;4字节&#x…

IR-825 Biotin,IR 825 Biotin,IR825 Biotin,IR-825可以进行修饰生物素基团

IR825 Biotin&#xff0c;IR 825 Biotin&#xff0c;IR-825 Biotin | 生物素IR825荧光染料&#xff0c; 荧光染料IR825生物素&#xff0c;IR-825近红外染料 | CAS&#xff1a;N/A | 纯度&#xff1a;95%1.IR825 Biotin试剂信息&#xff1a;CAS&#xff1a;N/A外观&#xff1a;固…

字符串的使用

数组字符串转换 joinToString列表转成字符串 val str list.joinToString(",")split(“,”)字符串转成列表 val list1 str.split(",")subString()字符串截取 substring(0,2)这个只含开头不含结尾&#xff0c;索引从0开始 substring(2)这个表示截掉前两…

Genymotion模拟器安装

1.本节引言 如果你符合下述三种情况的话,你可以考虑安装一个Genymotion Android模拟器: 没有真机调试,只能用模拟器 嫌SDK内置的AVD启动速度,运行速度慢 电脑配置还可以,最好4G内存以上 如果你满足上述三种情况的话,那么装个比真机还快的Genymotion吧! 官方给出的介绍:…

Go爬虫学习笔记

N002.02 Go分布式爬虫实战 开篇 学习三阶段 入门&#xff0c;照猫画虎底层&#xff0c;了解方方面面&#xff0c;深入阅读源码和书籍借助开源组件来进行复杂设计&#xff0c;窥探各个组件赋能业务 分布式系统&#xff1a; 扩展性一致性可用性高并发微服务 爬虫&#xff1…

Java8使用Lambda表达式(流式)快速实现List转map 、分组、过滤等操作

利用java8新特性&#xff0c;可以用简洁高效的代码来实现一些数据处理。1 数据准备1.1 定义1个Fruit对象package com.wkf.workrecord.work;import org.junit.Test;import java.math.BigDecimal; import java.util.ArrayList; import java.util.List;/*** author wuKeFan* date …

Framework源码面试——Handler与事件传递机制面试集合

Handler面试题 Handler的作用&#xff1a; 当我们需要在子线程处理耗时的操作&#xff08;例如访问网络&#xff0c;数据库的操作&#xff09;&#xff0c;而当耗时的操作完成后&#xff0c;需要更新UI&#xff0c;这就需要使用Handler来处理&#xff0c;因为子线程不能做更新…

软件回归测试是什么?

一、软件回归测试是什么? 软件回归测试作为软件生命周期的一个组成部分&#xff0c;在整个软件测试过程中占有很大的工作量比重&#xff0c;软件开发的各个阶段都会进行多次回归测试。回归测试是指修改了旧代码后&#xff0c;重新进行测试以确认修改没有引入新的错误或导致其…

Boosting三巨头:XGBoost、LightGBM和CatBoost(发展、原理、区别和联系,附代码和案例)

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Binder ——binder的jni注册和binder驱动

环境&#xff1a;Android 11源码Android 11 内核源码源码阅读器 sublime textbinder的jni方法注册zygote启动1-1、启动zygote进程zygote是由init进程通过解析init.zygote.rc文件而创建的&#xff0c;zygote所对应的可执行程序是app_process&#xff0c;所对应的源文件是app_mai…

因果推断12--dragonnet论文和代码学习

目录 论文 dragonnet 1介绍 2 Dragonnet 3定向正则化 4相关工作 5实验 6讨论 NN-Based的模型 dragonnet 如何更新参数 dragonnet的损失函数 CausalML Dragonnet类 论文代码 论文 dragonnet Adapting Neural Networks for the Estimation of Treatment Effects 应…

二叉搜索树的实现

什么是二叉搜索树1.若它的左子树不为空&#xff0c;那么左子树上所有节点都小于根节点2.若它的右子树不为空&#xff0c;那么右子树上所有节点都小于根节点3.它的左右子树也分别是二叉搜索树4.使用中序遍历结果是从小到大定义节点&#xff0c;使用静态内部类static class TreeN…

http组成及状态及参数传递

http组成及状态及参数传递 早期的网页都是通过后端渲染来完成的&#xff1a;服务器端渲染&#xff08;SSR&#xff0c;server side render&#xff09;&#xff1a; 客户端发出请求 -> 服务端接收请求并返回相应HTML文档 -> 页面刷新&#xff0c;客户端加载新的HTML文档&…

7综合项目 旅游网 【7.精选分类】

精选旅游人气旅游→收藏次数最高最新旅游→日期最新主题旅游→主题关键字相同在首页将精选的内容动态展示的实现分析首页中的精选包含“人气旅游”、“最新旅游”、“主题旅游”三个部分index.html//页面加载完成,发送ajax请求根据点击不同分类展示不同内容人气旅游→收藏次数最…

分享17个提升开发效率的工具“轮子”

本文是向大家介绍平时在开发中经常用到的小工具&#xff0c;它能够极大得提升我们的开发效率&#xff0c;能够解决平时开发中遇到的问题。前言在java的庞大体系中&#xff0c;其实有很多不错的小工具&#xff0c;也就是我们平常说的“轮子“。如果在我们的日常工作当中&#xf…