首页 技术 正文
技术 2022年11月14日
0 收藏 506 点赞 2,252 浏览 3687 个字

Blinn-Phong反射模型实践(web实现)

games101 第四次作业

最终完成带贴图的 Blinn-Phong 模型,产生光照效果

完成了

  1. 不带贴图的 Blinn-Phone 反射模型
  2. 带贴图的模型,但是纹理映射应用在顶点着色器上
  3. 带贴图的模型,纹理映射在片元着色器上

Blinn-Phone 光照模型

光照分为三种,分别为环境光,漫反射光和类镜面反射的高光。这分别对应三种反射,当光照射在物体表面,物体表面会发生相应的反射,将光反射到人眼当中,这样,人眼才能看见物体。

环境光简单理解为任何地方都有的一种光,光的颜色和强度相同,当然,现实可不是这样;漫反射在初中学过,在物体表面某个点发生漫反射后,从任何地方都能看到这个点且颜色强度都一样;高光反射类似光照到镜子上产生的高亮的效果。设置进入人眼的光为 L,漫反射为 Ld,高光反射为 Ls,环境反射为 La,可以有公式

\[\begin{equation}
L = L_a+L_d+Ls
\end{equation}
\]

环境反射

环境光由于任何地方的光照强度相同,因此有公式

\[L_a = k_a \times I_a
\]

ka 代表了当前影响因子,可以令其为颜色,Ia 是光照强度,所有光照强度一致

该图是仅仅使用环境光和贴图产生的效果,Ia 设置为了 0.39,ka 设置为 vec3(1.0, 1.0, 1.0)。

漫反射

初中物理可以了解到,漫反射光强度一样,这里定义的漫反射光,从任何地方看向反射的点,强度都一样

由下图可以看到,I 表示原始光照强度,在三维空间中,光照强度以球面的形式向外扩散,因此强度衰减与球面积有关,假如光照与物体表面的点距离为 r,则在点处的光强为I/r^2 ;光从正面照向一个物体和从侧面照向一个物体强度是不同的,从正面照强度最大,可以得到当前点应角度产生的影响为n*l;如果 n*l为负值,说明光在背面,无法被看到,因此有了下述图片展示的公式

Ld 表示漫反射强度,kd 表示影响因子,可以为颜色,I 为初始光照强度,r 为光源距离物体点的距离,n 为当前点的法向量,l 为点到光源的方向向量。将环境反射和漫反射加入程序中可得

单独和环境反射进行对比可以发现,牛牛变得更有轮廓了

高光反射

高光反射最为复杂,最重要的一点,当反射角与入射角大小一致时,光照强度最大。首先依下图所示定义多个变量,I 为点到光的方向向量,n 为点的法向量,v 为点到视角的方向向量。现在,我们知道,反射角与入射角一致时,强度最大,若此时反射角设为入射角大小,那么 v 与反射线的夹角越大,损失的光照越多,而 v 与反射线夹角可以转换为 n 与 h 的夹角。

h 叫半城向量,实际上就是 I 向量与 v 向量的角平分线的方向向量。此时因为 n 和 h 的夹角产生的光照损失值为 n * h,其他部分与漫反射一致。I / r^2表示光照随距离的缩减,ks 表示影响因子

将上述三种反射集合起来形成了 Blinn-Phone 反射模型,和前面两种对比,有了明显的高光

顶点着色器和片元着色器

在顶点着色器中,会遍历每个顶点的属性进行处理,此时的运算均是三角顶点的运算。WebGL 提供了 Shader 的加载,从顶点着色器中可以定义 vary 变量类型,并在片元着色器中接收

  • 如果传输的是顶点坐标,那么在片元着色器中接收的是三角形内部像素点的顶点坐标
  • 如果传输的是法向量,那么在片元着色器中接收到的是三角形内部经过插值后的法向量
  • 如果传输的是顶点颜色,那么在片元着色器中接收到的是颜色插值
  • 其他均如此,在片元着色器中会经过插值计算后产生逐像素的插值

逐顶点计算的反射代码

// 实现了自定义的颜色的 blinn-phong reflection
const vertexShader = `
// vec3 normal, uv,
varying vec3 vColor;
void main(){
// Ld 漫反射
vec3 lightPoint = vec3(3.0, 4.0, -3.0);
vec3 l = normalize(lightPoint - position);
float radius1 = distance(lightPoint, position);
// Kd
vec3 LdColor = vec3(1.0, 0.5, 0.7);
float Id = 1.0;
vec3 Ld = LdColor*(Id/radius1)*max(0.0, dot(normal,l)); // La 环境光
vec3 LaColor = vec3(1.0, 1.0, 1.0);
float Ia = 0.2;
// vec3 La = LaColor*Ia;
// 用自己的颜色
vec3 La = LdColor*Ia; // Ls 镜面反射光
vec3 LsColor = vec3(1.0, 1.0, 1.0);
float Is =1.0;
vec3 vs = normalize(cameraPosition-position);
vec3 hs = normalize(vs+l);
float p = 30.0;
vec3 Ls = LsColor*(Is/radius1)* pow(max(0.0, dot(normal, hs)), p); vColor = La + Ld + Ls; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;const fragmentShader = `
varying vec3 vColor;
void main(){
gl_FragColor = vec4(vColor, 1.0);
}
`;

逐片元计算的反射代码

//法线贴图
const vertexShader3 = `
// vec3 normal, uv,
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main(){
vPosition = position;
vUv = uv;
vNormal = normal; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`;//法线贴图处理
const fragmentShader3 = `
uniform sampler2D cowTexture;
uniform sampler2D cowNormalTexture;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main(){ vec4 map1 = texture2D(cowTexture, vUv);
vec3 selfColor = vec3(map1.x, map1.y, map1.z); // 法线贴图将改变 normal
float uvAfterU = (vUv.x*1024.0+1.0)/1024.0;
float uvAfterV = (vUv.y*1024.0+1.0)/1024.0;
float c1 = 1.0;
float c2 = 1.0;
vec4 currentDepth = texture2D(cowNormalTexture, vUv);
vec4 currentDepthU = texture2D(cowNormalTexture, vec2(uvAfterU, vUv.y));
vec4 currentDepthV = texture2D(cowNormalTexture, vec2(vUv.x, uvAfterV));
// float dp_u = c1 *
if(uvAfterU<=1.0 && uvAfterV<=1.0){ } // Ld 漫反射
vec3 lightPoint = vec3(3.0, 4.0, -3.0);
vec3 l = normalize(lightPoint - vPosition);
float radius1 = distance(lightPoint, vPosition);
// Kd
vec3 LdColor = vec3(1.0, 1.0, 1.0);
float Id = 1.0;
vec3 Ld = selfColor*(Id/radius1)*max(0.0, dot(vNormal,l));
//
// La 环境光
vec3 LaColor = vec3(1.0, 1.0, 1.0);
float Ia = 0.39;
// vec3 La = LaColor*Ia;
// 用自己的颜色
vec3 La = selfColor*Ia; // Ls 镜面反射光
vec3 LsColor = vec3(1.0, 1.0, 1.0);
float Is =1.0;
vec3 vs = normalize(cameraPosition-vPosition);
vec3 hs = normalize(vs+l);
float p = 30.0;
vec3 Ls = selfColor*(Is/radius1)* pow(max(0.0, dot(vNormal, hs)), p); // 整体颜色
vec3 vColor = La + Ld + Ls;
// vec3 vColor = La + Ld ;
// vec3 vColor = vec3(1.0, 0.6, 0.8); gl_FragColor = vec4(vColor, 1.0);
}
`;

在之前的实践中采用了纯 CPU 的计算方式,原生 canvas API 渲染,但是效率和处理太慢,因此,后续的实验采用 threejs 框架加快实践效率

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,088
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,564
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,413
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,186
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:7,822
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:4,905