什么是着色器 Shader

发布于:2025-05-18 ⋅ 阅读:(31) ⋅ 点赞:(0)

在这里插入图片描述

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

本文围绕 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 光照模型 + 纹理融合 示例,非常值得你自己调试、注释、修改看看不同效果。


网站公告

今日签到

点亮在社区的每一天
去签到