什么是着色器 Shader

news2025/5/20 14:40:03

在这里插入图片描述

本人就是图形学结课了,对 OpenGL着色器还有很多疑问嘿嘿

文章目录

  • 为什么要有着色器
  • vshader
  • fshader

本文围绕 vshader 和 fshader 代码示例讲解。

(着色器代码取自本人简单OpenGL项目 https://github.com/DBWGLX/-OpenGL-3D-Lighting-and-Shadow-Modeling)

为什么要有着色器

着色器代码其实是给 GPU 跑的。

我们玩 3A 大作,每时每刻画面中大量角色物体可能都在交互变化,并且会因光源而表现不同(高光,明暗)。
时时刻刻计算这么多,CPU 几个核,再加MIPS(每秒百万指令)指令集也根本跑不完。

但是这些计算其实都是简单矩阵间计算,那我们可以换更多的简单核来跑,既不需要太复杂又足够计算 —— GPU

GPU 擅长做 大规模的矩阵运算


  • vshader 负责把物体规划好【模型空间 -> 世界空间 -> 相机空间 -> 屏幕空间】
  • fshader 结合光照和纹理,对显示器屏幕上的每个“像素点”计算最终颜色【给每个像素上色】

vshader

Vertex Shader 顶点着色器

【顶点着色器】是图形管线的第一阶段,作用是接收每个顶点的信息(如位置、颜色、法线、投影等),对其进行计算,并输出一些信息供后续的【片元着色器】使用。

比如三位建模的物体移动,模型的所有点、面都要移动,我们提供坐标给GPU让他来算位置。(当然还有透视投影,跟多关于 “投影和相机” 可以阅读以了解:https://blog.csdn.net/JK01WYX/article/details/143242785)

下面代码的 in 变量就是我们 cpp 程序提前绑定的,out 就是给 fshader 的,uniform(统一的)就是绑定好在vshader里 大家都用来计算的(相机坐标系,投影方式等)。

#version 330 core // GLSL(OpenGL Shading Language),版本是 #version 330 core,即 OpenGL 3.3 核心版本。

// 顶点着色器
in vec3 vPosition;	// 顶点位置,3D坐标
in vec3 vColor;		// 颜色值(如 RGB)
in vec3 vNormal;	// 法向量,通常用于光照计算
in vec2 vTexture;	// 纹理坐标,通常范围在 [0, 1]

// 传给片元着色器的变量
out vec3 position;
out vec3 normal;
out vec3 color;
out vec2 texCoord;	// texture coordinate 纹理坐标

// 模型变换矩阵、相机观察矩阵、投影矩阵
uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main() 
{
	// 把顶点位置乘以模型矩阵,变换到世界空间。 // vec4() 是把 3D 坐标变成 4D 齐次坐标。
	vec4 v1 = model * vec4(vPosition, 1.0);  

	// 由于model矩阵有可能为阴影矩阵,为了得到正确位置,我们需要做一次透视除法
	vec4 v2 = vec4(v1.xyz / v1.w, 1.0);
	
	// 先应用视图矩阵(把世界空间转换到相机空间),再应用投影矩阵
	vec4 v3 = projection* view * v2;
	
	// 最终设置到 gl_Position,这是 GPU 用来决定顶点【最终在屏幕上位置】的变量
	gl_Position = v3;
	
	
	// 最后:传递变量给片元着色器
    position = vec3(v2.xyz);
    normal = vec3( (model *  vec4(vNormal, 0.0)).xyz ); // 法线向量是方向,不需要平移,所以 w = 0.0。
	color = vColor;

	texCoord = vTexture;

}

fshader

我当时加载了三角面片和四角面片的模型,自己去做了适配。

甚至后面在 B站的模之屋 下载了多网格并各自带纹理的模型,去 Blender 转成 .obj 去加载 (当然失败,纹理这块)

其实也完全可以写多个着色器的。

#version 330 core

// 光源结构体
struct Light{
	vec4 ambient;	// 环境光颜色(整个空间的背景亮度)
	vec4 diffuse;	// 漫反射颜色(光打到表面后均匀反射)
	vec4 specular;	// 镜面高光颜色(发亮的高光)
	vec3 position;	// 光源的位置
	
    // 控制光照距离衰减的三个参数
    float constant; // 常数项
    float linear;	// 一次项
    float quadratic;// 二次项
};

// 材质属性结构体
struct Material{
	// 物体本身对应三种反射光的颜色属性
	vec4 ambient;
	vec4 diffuse;
	vec4 specular;
	
	// 镜面反射的“锐利程度”(越大越亮 越小越模糊)
	float shininess;
};

// In
in vec3 position;
in vec3 normal;
in vec3 color;
in vec2 texCoord;



uniform vec3 eye_position;	// 相机坐标
uniform Light light;		// 光源

uniform Material material;	// 物体材质
uniform int isShadow;		// 是否为阴影
uniform int hasTextureMap;	// 是否使用纹理
uniform sampler2D texture;  // 纹理采样器

// 这是片元最终的颜色结果,会输出到屏幕上。
out vec4 fColor;

void main()
{
	if (isShadow == 1) {	
		// 如果当前是画阴影贴图,就直接涂成黑色,不用算光照。
		fColor = vec4(0.0, 0.0, 0.0, 1.0);
	}
	else {
		// 将顶点坐标、光源坐标和法向量转换到相机坐标系
		vec3 norm = (vec4(normal, 0.0)).xyz;
		vec3 N = normalize(norm);						// 法向量
		vec3 L = normalize(light.position - position);	// 光源方向
		//vec3 L = normalize(light.position);			// 平行光
		vec3 V = normalize(eye_position - position);	// 视角方向
		vec3 R = reflect(-L, N);						// 反射方向
		vec3 H = normalize(L + V);						// 半角向量(可选)

		vec4 I_a = light.ambient * material.ambient;	// 环境光 = 光源的环境光 × 材质的环境反射能力

		// @TODO: Task2 计算系数和漫反射分量I_d			// 漫反射 = max(光线方向·法线方向, 0) × 光的亮度 × 材质漫反射颜色
		float diffuse_dot = max(dot(L, N), 0.0);
		vec4 I_d = diffuse_dot * light.diffuse * material.diffuse;

		// @TODO: Task2 计算系数和镜面反射分量I_s			// 镜面高光 = 视角方向·反射方向的夹角
		float specular_dot_pow = pow(max(dot(V, R), 0.0), material.shininess);// shininess 越大,高光越小越亮,表示光滑表面
		// float specular_dot_pow = pow( clamp(dot(V, R), 0.0, 1.0), material.shininess );
		vec4 I_s = specular_dot_pow * light.specular * material.specular;
		// 光在背面就不该出现高光效果
		if (dot(L, N) < 0.0) {
			I_s = vec4(0.0, 0.0, 0.0, 1.0);
		}


		fColor = vec4(color, 1.0);
		if (hasTextureMap==1) {
			//纹理基础颜色
			vec4 textureColor = texture2D(texture, texCoord);
			// 合并三个分量的颜色,修正透明度
			fColor = textureColor;
		}

		// 总结三种光照并输出颜色 : 最终颜色 = 材质色 × (环境光 + 漫反射 + 镜面反射)
		fColor *= I_a + I_d + I_s;

		fColor.a = 1.0; // 保证不透明
	}
}

这段 shader 是个非常典型的 Phong 光照模型 + 纹理融合 示例,非常值得你自己调试、注释、修改看看不同效果。

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

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

相关文章

Redis的主从架构

主从模式 全量同步 首先主从同步过程第一步 会先比较replication id 判断是否是第一次同步假设为第一次同步 那么就会 启动bgsave异步生成RDB 同时fork子进程记录生成期间的新数据发送RDB给从节点 清空本地数据写入RDB 增量同步 对比ReplicationID不同因此选择增量同步在Rep…

博客系统功能测试

博客系统网址&#xff1a;http://8.137.19.140:9090/blog_list.html 主要测试内容 功能测试、界面测试、性能测试、易用性测试、安全测试、兼容性测试、弱网测试、安装卸载测试、压力测试… 测试方法及目的 利用selenium和python编写测试脚本&#xff0c;对博客系统进行的相关…

【深度学习新浪潮】什么是多模态大模型?

多模态大模型是人工智能领域的前沿技术方向,它融合了多种数据模态(如文本、图像、语音、视频、传感器数据等),并通过大规模参数模型实现跨模态的联合理解与生成。简单来说,这类模型就像人类一样,能同时“看”“听”“读”“说”,并将不同信息关联起来,完成复杂任务。 …

机器学习前言2

1.机器学习 2.机器学习模型 3.模型评价方法 4.如何选择合适的模型 介绍 机器学习&#xff08;Machine Learning, ML&#xff09;是人工智能&#xff08;AI&#xff09;的核心分支&#xff0c;致力于通过数据和算法让计算机系统自动“学习”并改进性能&#xff0c;而无需显式编…

【成品设计】基于Arduino的自动化农业灌溉系统

《基于STM32的单相瞬时值反馈逆变器》 硬件设计&#xff1a; ESP-C3最小系统板&#xff1a;主控芯片&#xff0c;内部集成wifi。土壤湿度传感器&#xff1a;采集土壤湿度。温度传感器&#xff1a;采集土壤温度。水泵模块&#xff1a;水泵继电器软管。按键3个&#xff1a;参数…

前端页面 JavaScript数据交互

前言&#xff1a;学习JavaScript为前端设置动态效果&#xff0c;实现交互。JavaScript是一种广泛应用于网页开发的脚本语言&#xff0c;它能够使网页变得更加动态和交互性。作为一种客户端脚本语言&#xff0c;JavaScript可以被嵌入到HTML中&#xff0c;并且可以被所有现代的网…

esp32课设记录(三)mqtt通信记录 附mqtt介绍

目录 安装mqttx&#xff08;云端部署&#xff09; 安装mosquitto&#xff08;本地部署&#xff09; 编程&#xff0c;连接wifi 编程&#xff0c;连接mqtt&#xff0c;实现数据接收 实际效果展示&#xff1a; 附录&#xff1a;mqtt介绍 工作流程简述&#xff1a; 工作流…

string类(详解)

【本节目标】 1. 为什么要学习string类 2. 标准库中的string类 3. string类的模拟实现 4. 扩展阅读 1. 为什么学习string类&#xff1f; 1.1 C语言中的字符串 C 语言中&#xff0c;字符串是以 \0 结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c; C 标准库中提供…

MATLAB | R2025a 更新了哪些有趣的东西?

千呼万唤始出来&#xff0c;MATLAB R2025A 来见面&#xff0c;这次更新比往常晚了两个月&#xff0c;让我们看看更了哪些好玩的新东西叭&#xff1a;首先下载更新启动一气呵成&#xff0c;映入眼帘的是&#xff1a; 1 基本界面 基本界面变得和 MATLAB 网页版一模一样了&#…

前缀和——和为K的子数组

作者感觉本题稍稍有点难度&#xff0c;看了题解也思考了有一会TWT 显然&#xff0c;暴力我们是不可取的&#xff0c;但这里我们可以采取一种新的遍历数组形式&#xff0c;从后向前&#xff0c;也就是以i位置为结尾的所有子数组&#xff0c;这个子数组只统计i位置之前的。 然后…

深入理解 ZAB:ZooKeeper 原子广播协议的工作原理

目录 ZAB 协议&#xff1a;ZooKeeper 如何做到高可用和强一致&#xff1f;&#x1f512;ZAB 协议的核心目标 &#x1f3af;ZAB 协议的关键概念 &#x1f4a1;ZAB 协议的运行阶段 &#x1f3ac;阶段一&#xff1a;Leader 选举 (Leader Election) &#x1f5f3;️阶段二&#xff…

GraphPad Prism项目的管理

《2025新书现货 GraphPad Prism图表可视化与统计数据分析&#xff08;视频教学版&#xff09;雍杨 康巧昆 清华大学出版社教材书籍 9787302686460 GraphPadPrism图表可视化 无规格》【摘要 书评 试读】- 京东图书 GraphPad Prism统计数据分析_夏天又到了的博客-CSDN博客 项目…

驱动-Linux定时-timer_list

了解内核定时相关基础知识 文章目录 简要介绍timer_list 特点API 函数实验测试程序 - timer_mod.c编译文件-Makefile实验验证 注意事项总结 简要介绍 硬件为内核提供了一个系统定时器来计算流逝的时间&#xff08;即基于未来时间点的计时方式&#xff0c; 以当前时刻为计时开始…

STM32F103_LL库+寄存器学习笔记22 - 基础定时器TIM实现1ms周期回调

导言 如上所示&#xff0c;STM32F103有两个基本定时器TIM6与TIM7&#xff0c;所谓「基本定时器」&#xff0c;即功能最简单的定时器。 项目地址&#xff1a; github: LL库: https://github.com/q164129345/MCU_Develop/tree/main/stm32f103_ll_library22_Basic_Timer寄存器方…

5个yyds的.Net商城开源项目

今天一起来盘点下5个商城开源项目。 1、支持多语言、多商店的商城&#xff0c;.Net7 EF7领域驱动设计架构&#xff08;Smartstore&#xff09; 项目简介 Smartstore 支持桌面和移动平台、多语言、多商店、多货币的商城&#xff0c;并支持SEO优化&#xff0c;支持无限数量的…

[项目深挖]仿muduo库的并发服务器的解析与优化方案

标题&#xff1a;[项目深挖]仿muduo库的并发服务器的优化方案 水墨不写bug 文章目录 一、buffer 模块&#xff08;1&#xff09;线性缓冲区直接扩容---->环形缓冲区定时扩容&#xff08;只会扩容一次&#xff09;&#xff08;2&#xff09;使用双缓冲&#xff08;Double Buf…

国标GB28181视频平台EasyGBS校园监控方案:多场景应用筑牢安全防线,提升管理效能

一、方案背景​ 随着校园规模不断扩大&#xff0c;传统监控系统因设备协议不兼容、数据分散管理&#xff0c;导致各系统之间相互独立、数据无法互通共享。在校园安全防范、教学管理以及应急响应过程中&#xff0c;这种割裂状态严重影响工作效率。国标GB28181软件EasyGBS视频云…

SHIMADZU岛津 R300RC300 Operation Manual

SHIMADZU岛津 R300RC300 Operation Manual

使用 Docker 部署 React + Nginx 应用教程

目录 1. 创建react项目结构2. 创建 .dockerignore3. 创建 Dockerfile4. 创建 nginx.conf5. 构建和运行6. 常用命令 1. 创建react项目结构 2. 创建 .dockerignore # 依赖目录 node_modules npm-debug.log# 构建输出 dist build# 开发环境文件 .git .gitignore .env .env.local …

API Gateway REST API 集成 S3 服务自定义 404 页面

需求分析 使用 API Gateway REST API 可以直接使用 S3 作为后端集成对外提供可以访问的 API. 而当访问的 URL 中存在无效的桶, 或者不存在的对象时, API Gateway 默认回向客户端返回 200 状态码. 而实际上这并不是正确的响应, 本文将介绍如何自定义返回 404 错误页面. 基本功…