Learn OpenGL 03 着色器

GLSL

着色器的开头总是要声明版本,接着是输入和输出变量、uniform和main函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。

一个典型的着色器有下面的结构:

#version version_number
in type in_variable_name;
in type in_variable_name;out type out_variable_name;uniform type uniform_name;int main()
{// 处理输入并进行一些图形操作...// 输出处理过的结果到输出变量out_variable_name = weird_stuff_we_processed;
}

数据类型 

GLSL中包含C等其它语言大部分的默认基础数据类型:intfloatdoubleuintbool。GLSL也有两种容器类型,分别是向量(Vector)和矩阵(Matrix)

向量

GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n代表分量的数量):

类型含义
vecn包含n个float分量的默认向量
bvecn包含n个bool分量的向量
ivecn包含n个int分量的向量
uvecn包含n个unsigned int分量的向量
dvecn包含n个double分量的向量

大多数时候我们使用vecn,因为float足够满足大多数要求了。

向量这一数据类型也允许一些有趣而灵活的分量选择方式,叫做重组(Swizzling)。重组允许这样的语法:

vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

你可以使用上面4个字母任意组合来创建一个和原来向量一样长的(同类型)新向量,只要原来向量有那些分量即可;然而,你不允许在一个vec2向量中去获取.z元素。我们也可以把一个向量作为一个参数传给不同的向量构造函数,以减少需求参数的数量:

vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);

输入与输出

顶点着色器从顶点数据中直接接收输入。为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。

片段着色器,它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

如果我们打算从一个着色器向另一个着色器发送数据,我们必须在发送方着色器中声明一个输出,在接收方着色器中声明一个类似的输入。当类型和名字都一样的时候,OpenGL就会把两个变量链接到一起,它们之间就能发送数据了(这是在链接程序对象时完成的)。

Uniform

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

x修改Uniform的值:

首先需要找到着色器中uniform属性的索引/位置值。当我们得到uniform的索引/位置值后,我们就可以更新它的值了。这次我们不去给像素传递单独一个颜色,而是让它随着时间改变颜色:

如果glGetUniformLocation返回-1就代表没有找到这个位置值。最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。

float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f)

因为OpenGL在其核心是一个C库,所以它不支持类型重载,在函数参数不同的时候就要为其定义新的函数;glUniform是一个典型例子。

uniform对于设置一个在渲染迭代中会改变的属性是一个非常有用的工具,它也是一个在程序和着色器间数据交互的很好工具

更多属性!

顶点数据中新加了颜色属性

float vertices[] = {// 位置              // 颜色0.5f, -0.5f, 0.0f,  1.0f, 0.0f, 0.0f,   // 右下-0.5f, -0.5f, 0.0f,  0.0f, 1.0f, 0.0f,   // 左下0.0f,  0.5f, 0.0f,  0.0f, 0.0f, 1.0f    // 顶部
};

主要的变化就是要重新配置顶点属性指针

glVertexAttribPointer函数的前几个参数比较明了。这次我们配置属性位置值为1的顶点属性。颜色值有3个float那么大,我们不去标准化这些值。

由于我们现在有了两个顶点属性,我们不得不重新计算步长值。为获得数据队列中下一个属性值(比如位置向量的下个x分量)我们必须向右移动6个float,其中3个是位置值,另外3个是颜色值。这使我们的步长值为6乘以float的字节数(=24字节)。
同样,这次我们必须指定一个偏移量。对于每个顶点来说,位置顶点属性在前,所以它的偏移量是0。颜色属性紧随位置数据之后,所以偏移量就是3 * sizeof(float),用字节来计算就是12字节。

// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);

创建自己的着色器类并且从文件读取

创建一个Shader.h和Shader.cpp两个文件

Shader.h文件主要存放Shader类和其中的函数声明,方便随时查看其中函数的功能

Shader.cpp文件主要存放函数的定义

要从文件读取着色器必须使用使用C++文件流

CPU不能直接读取硬盘中的内容,必须先移动到内存中,也就是要经过硬盘-->FileBuffer-->StringBuffer,之后因为OpenGL不操作String,操作的是char数组,所以我们要一个String的缓存把StringBuffer里的东西变成char数组。

Shader.h

#pragma once
#include <string>
class Shader
{
public:Shader(const char* vertexPath, const char* fragmentPath); // 构造器读取并构建着色器std::string vertexString;std::string fragmentString;const char* vertexSource;const char* fragmentSource;unsigned int ID;  //Shader Program IDvoid use();  // 使用/激活程序// uniform工具函数void setBool(const std::string& name, bool value) const;void setInt(const std::string& name, int value) const;void setFloat(const std::string& name, float value) const;
private:void checkCompileErrors(unsigned int ID, std::string type);//检查着色器或者程序是否编译错误
};

Shader.cpp

#include "Shader.h"
#include <iostream>
#include <fstream>
#include <SStream>#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <iostream>using namespace std;Shader::Shader(const char* vertexPath, const char* fragmentPath)
{//从文件路径中获取顶点/片段着色器ifstream vertexFile;ifstream fragmentFile;stringstream vertexSStream;stringstream fragmentSStream;//打开文件vertexFile.open(vertexPath);//与硬盘文件建立链接fragmentFile.open(fragmentPath);//保证ifstream对象可以抛出异常:vertexFile.exceptions(ifstream::failbit || ifstream::badbit);//检查是否打开失败或者读取到了坏文件fragmentFile.exceptions(ifstream::failbit || ifstream::badbit);try{if (!vertexFile.is_open() || !fragmentFile.is_open()){throw exception("open file error");}//读取文件缓冲内容到数据流vertexSStream << vertexFile.rdbuf();//硬盘-->FileBuffer-->StringBufferfragmentSStream << fragmentFile.rdbuf();//转换数据流到stringvertexString = vertexSStream.str();fragmentString = fragmentSStream.str();vertexSource = vertexString.c_str();fragmentSource = fragmentString.c_str();// 编译着色器unsigned int vertex, fragment;// 顶点着色器vertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vertexSource, NULL);glCompileShader(vertex);checkCompileErrors(vertex, "VERTEX");// 片段着色器fragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fragmentSource, NULL);glCompileShader(fragment);checkCompileErrors(fragment, "FRAGMENT");// 着色器程序ID = glCreateProgram();glAttachShader(ID, vertex);glAttachShader(ID, fragment);glLinkProgram(ID);checkCompileErrors(ID, "PROGRAM");// 删除着色器,它们已经链接到我们的程序中了,已经不再需要了glDeleteShader(vertex);glDeleteShader(fragment);}catch (const std::exception& ex){printf(ex.what());}
}void Shader::use()
{glUseProgram(ID);
}void Shader::checkCompileErrors(unsigned int ID, std::string type) {int sucess;char infolog[512];if (type != "PROGRAM"){glGetShaderiv(ID, GL_COMPILE_STATUS, &sucess);if (!sucess){glGetShaderInfoLog(ID, 512, NULL, infolog);cout << "shader compile error:" << infolog << endl;}}else{glGetProgramiv(ID, GL_LINK_STATUS, &sucess);if (!sucess){glGetProgramInfoLog(ID, 512, NULL, infolog);cout << "program linking error:" << infolog << endl;}cout << "编译成功!" << endl;}
}

main.cpp

只需要加上两句代码就可以了,这里的两个txt文件需要自己创建一个,之后就可以循环使用了

  Shader* myshader = new Shader("vertexSource.txt", "fragmentSource.txt");
[...]myshader->use();

练习

1.修改顶点着色器让三角形上下颠倒:参考解答

将顶点着色器输出的y坐标取反即可

#version 330 core									  
layout(location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec4 vertexColor;								   
void main(){										   gl_Position = vec4(aPos.x, -aPos.y, aPos.z, 1.0);   vertexColor = vec4(aColor,1.0);					   
}

2.使用uniform定义一个水平偏移量,在顶点着色器中使用这个偏移量把三角形移动到屏幕右侧:参考解答

顶点着色器的x坐标加上一个随时间增加的偏移量即可

//main.cpp
myshader->setFloat("offset", 0.01+glfwGetTime());#version 330 core									  
layout(location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec4 vertexColor;	
uniform float offset;
void main(){										   gl_Position = vec4(aPos.x+offset, aPos.y, aPos.z, 1.0);   vertexColor = vec4(aColor,1.0);					   
}

3.使用out关键字把顶点位置输出到片段着色器,并将片段的颜色设置为与顶点位置相等(来看看连顶点位置值都在三角形中被插值的结果)。做完这些后,尝试回答下面的问题:为什么在三角形的左下角是黑的?:参考解答

三角形的左下角是黑的,是因为左下角顶点(-0.5,-0.5,0),因为颜色值是0到1,负值会被截断到0,就是黑色。插值以后靠近左下角的地方就是黑色的

vertex 

#version 330 core									  
layout(location = 0) in vec3 aPos;   // 位置变量的属性位置值为 0
layout(location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec4 vertexColor;	
out vec3 pos;
uniform float offset;
void main(){										   gl_Position = vec4(aPos.x+offset, aPos.y, aPos.z, 1.0);   vertexColor = vec4(aColor,1.0);		pos = aPos;
}

fragment

#version 330 core			
in vec4 vertexColor;			
out vec4 FragColor;	
in vec3 pos;
void main(){				FragColor = vec4(pos,1);}

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

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

相关文章

O2OA(翱途)开发平台系统安全-用户登录IP限制

O2OA(翱途)开发平台[下称O2OA开发平台或者O2OA]支持对指定的用户设置可以连接的客户端计算机的IP地址&#xff0c;以避免用户在不安全的环境下访问系统。本篇主要介绍如何开启O2OA用户登录IP限制。 一、先决条件&#xff1a; 1、O2Server服务器正常运行&#xff0c;系统安装部…

PostgreSQL开发与实战(6.3)体系结构3

作者&#xff1a;太阳 四、物理结构 4.1 软件安装目录 bin //二进制可执行文件 include //头文件目录 lib //动态库文件 share //文档以及配置模版文件4.2 数据目录 4.2.1 参数文件 pg_hba.conf //认证配置文件 p…

C++ string详解+模拟实现

文章目录 写在前面1. string类的使用1.1 string类的构造1.2 string类的迭代器1.3 string类对象的容量操作1.4 string类对象的修改操作1.5 string类对象的非成员函数 2. string类的模拟实现2.1 模拟实现string类的默认成员函数2.2 模拟实现string类的迭代器2.3 模拟实现string类…

如何本地部署SeaFile文件共享服务并实现无公网IP访问内网本地文件

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 现在我们身边的只能设备越来越多&#xff0c…

JavaWeb后端——分层解耦 IOC DI

分层/三层架构概述 三层架构&#xff1a;Controller、Service、Dao 解耦/IOC&DI概述 分层解耦 容器称为&#xff1a;IOC容器/Spring容器 IOC 容器中创建&#xff0c;管理的对象&#xff0c;称为&#xff1a;bean 对象 IOC&DI入门 实现 IOC&DI 需要的注解&#…

Python基于大数据的豆瓣电影分析,豆瓣电影可视化系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

大模型应用开发-大模型token等基本概念及参数和内存的关系

大模型相关目录 大模型&#xff0c;包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步&#xff0c;扬帆起航。 大模型应用向开发路径及一点个人思考大模型应用开发实用开源项目汇总大模型问答项目…

深度学习_20_卷积中的填充与步幅

如果图片本身比较小&#xff0c;卷积之后输出也会很小&#xff0c;那么可以在图片与卷积核相乘之前先填充一下&#xff0c;让输出为预期大小 一般填充后输入&#xff0c;输出相同 当图片比较大的时候&#xff0c;如果利用卷积核去得到我们想要的大小的话&#xff0c;得用到多层…

lab3090连接

淘宝安装包&#xff0c;镜像包放在了F盘&#xff0c;文件夹名为“torch” 远程连接服务器 服务器&#xff0c;192.168.7.194&#xff0c;端口1324&#xff0c;账号&#xff0c;llf&#xff0c;密码123456 进入容器&#xff1a; docker attach llf_pytorch 创建后端jupyte…

基础---nginx 启动不了,跟 Apache2 服务冲突

文章目录 查看 nginx 服务状态nginx 启动后 访问页面 127.0.0.1停止 nginx 服务&#xff0c;访问不了页面停止/启动 Apache2 服务&#xff0c;启动 Apache2 页面访问显示正确nginx 莫名启动不了卸载 Apache2 服务器 启动 nginx &#xff0c;但是总是不能实现反向代理&#xff0…

提升数据分析效率,选择IBM SPSS Statistics专业统计分析软件

在当今信息爆炸的时代&#xff0c;数据已经成为决策的重要依据。对于研究人员、学者、企业管理者等群体来说&#xff0c;如何高效地进行数据分析并得出准确结论至关重要。而IBM SPSS Statistics作为一款专业统计分析软件&#xff0c;为用户提供了强大的工具和功能&#xff0c;助…

阿里云免费证书改为3个月,应对方法很简单

情商高点的说法是 Google 积极推进90天免费证书&#xff0c;各服务商积极响应。 情商低点的话&#xff0c;就是钱的问题。 现在基本各大服务商都在2024年停止签发1年期的免费SSL证书产品&#xff0c;有效期都缩短至3个月。 目前腾讯云倒还是一年期。 如果是一年期的话&#x…

java maven聚合项目-子项目重写父项目的依赖版本无法生效

前言 项目的springboot版本2.6.13 项目主pom 引入了springboot的依赖配置 <dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><…

泽众云真机-机型支持ADB调试功能即将上线

最近云真机平台在线客服&#xff0c;收到很多咨询关于ADB调试功能&#xff0c;什么时候能更新&#xff1f;据小编所知&#xff0c;正在升级之中&#xff0c;有一块专门为了解决ADB调试功能提前准备&#xff0c;升级网络硬件设备&#xff0c;目前平台的功能已开发完成&#xff0…

#QT(一种朴素的计算器实现方法)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a;这是全靠自己想法写的计算器&#xff0c;没有参考任何教程。 &#xff08;1&#xff09;这个计算器只要有运算符敲入就会进行一次运算&#xff0c;所以他没有先后之后&#xff0c;无法满足运算优先级。 &#xff08;2&#xff…

[Redis]——Redis持久化的两种方式RDB、AOF

目录 RDB快照模式 概念&#xff1a; 触发时机&#xff1a; 异步做快照 AOF追加模式 概念&#xff1a; 触发时机&#xff1a; bgrewriteaof命令&#xff1a; 比较两种模式&#xff1a; RDB快照模式 概念&#xff1a; RDB模式就是保存当前Redis的状态到本地磁盘文件&am…

AttributeError: cannot assign module before Module.__init__() call

原因 调用了自定义的类&#xff0c;但是在自定义的类的__init__函数下面没有写super( XXX, self ).init() 错误案例 import torch import torch.nn as nnclass SelfAttention(nn.Module):""" Self-Attention """def __init__(self, n_head, d…

【vue.js】文档解读【day 4】 | 事件处理

如果阅读有疑问的话&#xff0c;欢迎评论或私信&#xff01;&#xff01; 文章目录 事件处理前言监听事件内联事件处理器方法事件处理器方法与内联事件判断在内联处理器中调用方法在内联事件处理器中访问事件参数修饰符事件修饰符按键修饰符常规按键别名系统按键别名组合按键ex…

3d场景重建图像渲染 | 神经辐射场NeRF(Neural Radiance Fields)

神经辐射场NeRF&#xff08;Neural Radiance Fields&#xff09; 概念 NeRF&#xff08;Neural Radiance Fields&#xff0c;神经辐射场&#xff09;是一种用于3D场景重建和图像渲染的深度学习方法。它由Ben Mildenhall等人在2020年的论文《NeRF: Representing Scenes as Neur…

一、C#冒泡排序算法

一、C#冒泡排序算法 简介 冒泡排序算法是一种基础的排序算法&#xff0c;它的实现原理比较简单。核心思想是通过相邻元素的比较和交换来将最大&#xff08;或最小&#xff09;的元素逐步"冒泡"到数列的末尾。 实现原理 冒泡排序是一种简单的排序算法&#xff0c;其…