先附图一张,所有矩阵变换的坐标系变换结果。
所有光源代码思想:
// 每种光经过一种处理。这里举最复杂的聚光灯的例子,光源的处理主要是在fragment中,举了fragment shader的例子。但由于所有位置都是基于世界坐标的,所以顶点坐标要经过处理变换为世界坐标。(我也不知道为什么纹理不需要变成世界坐标系。。因为是自学,也没有老师问。。。。)。法向量因为是向量,所以只需要经过法线矩阵转换反缩放就好
材质结构体{ // 以下都是uniform型的,传参以接收
环境光 // 由纹理图片设置 sampler2D
漫反射光
镜面反射光
发光值 // 设置镜面反射光的光值大小
}
Uniform 材质 材质;
光源结构体{
视线向量 // 摄像头的方向向量
光源位置
环境光光强度 // 也就是弱化了多少的颜色
漫反射光光强度
镜面反射光光强度
光源衰减三参数 // 衰减公式三参数(float型),光源才有
内圆锥范围
外圆锥范围 // 聚光灯才有
}
Uniform 光源 光源
接收法向量
接收世界向量
接收纹理坐标
Main{
单个光源方法(); // 计算系数,然后由系数和该材质相乘得到最终渲染
// 所有颜色相加得到最终的fragment的值。
}
聚光灯光源方法(光源,法向量,顶点世界位置,视线向量){
// 计算光源衰减值
距离 = length(光源位置 - 顶点位置)
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); // 公式计算
// 计算聚光灯值
由视线向量和光线向量点乘得到余弦值
float epsilon = light.cutOff - light.outerCutOff;
float intensity = clamp((theta - light.outerCutOff) / epsilon, 0.0, 1.0); // 公式计算
// 计算入射光
光源位置 - 顶点坐标(标准化)
// 计算环境光
纹理和环境光强结合
// 计算漫反射光
由入射光和法向量点乘得到。然后和漫反射光强结合,再与纹理结合
// 计算镜面反射光
由入射光反射得到,然后和视线向量点乘。然后和镜面反射光强结合,再和纹理结合。
// 处理
将三种光和衰减值和聚光灯光锥结合,即漫反射 = 漫反射*衰减值*光锥值; // 得到聚光灯的颜色
}