1. Phong光照模型
Phong光照模型是一种经典的光照模型,用于模拟真实世界中的光照效果,它将光照分为环境光(Ambient)、漫反射光(Diffuse)和镜面反射光(Specular)三个部分。最终的光照颜色是这三部分光照颜色的总和。
Phong Reflection Model 来自 维基百科上的数据
下面详细介绍各部分的数学公式及整体公式。
1.1. 环境光(Ambient)
环境光模拟了场景中全局的、均匀的光照,它不依赖于光源的位置和物体的朝向。环境光的计算公式如下:
I a m b i e n t = k a × I a I_{ambient} = k_a \times I_a Iambient=ka×Ia
其中:
- I a m b i e n t I_{ambient} Iambient 是环境光的颜色强度。
- k a k_a ka 是物体的环境光反射系数,取值范围通常在 [ 0 , 1 ] [0, 1] [0,1] 之间,它表示物体对环境光的反射能力。
- I a I_a Ia 是环境光的颜色强度。
1.2. 漫反射光(Diffuse)
漫反射光模拟了光线在物体表面的均匀散射,它取决于光线的方向和物体表面法线的夹角。漫反射光的计算公式基于 Lambert 余弦定律:
I d i f f u s e = k d × I l × max ( 0 , N ⋅ L ) I_{diffuse} = k_d \times I_l \times \max(0, \mathbf{N} \cdot \mathbf{L}) Idiffuse=kd×Il×max(0,N⋅L)
其中:
- I d i f f u s e I_{diffuse} Idiffuse 是漫反射光的颜色强度。
- k d k_d kd 是物体的漫反射系数,取值范围通常在 [ 0 , 1 ] [0, 1] [0,1] 之间,它表示物体对漫反射光的反射能力。
- I l I_l Il 是光源的颜色强度。
- N \mathbf{N} N 是物体表面的法线向量,且为单位向量。
- L \mathbf{L} L 是从物体表面指向光源的单位向量。
- N ⋅ L \mathbf{N} \cdot \mathbf{L} N⋅L 是 N \mathbf{N} N 和 L \mathbf{L} L 的点积, max ( 0 , N ⋅ L ) \max(0, \mathbf{N} \cdot \mathbf{L}) max(0,N⋅L) 确保结果不会为负数。
1.3. 镜面反射光(Specular)
镜面反射光模拟了光线在物体表面的镜面反射效果,它取决于观察者的位置。镜面反射光的计算公式如下:
I s p e c u l a r = k s × I l × ( max ( 0 , R ⋅ V ) ) n I_{specular} = k_s \times I_l \times (\max(0, \mathbf{R} \cdot \mathbf{V}))^n Ispecular=ks×Il×(max(0,R⋅V))n
其中:
- I s p e c u l a r I_{specular} Ispecular 是镜面反射光的颜色强度。
- k s k_s ks 是物体的镜面反射系数,取值范围通常在 [ 0 , 1 ] [0, 1] [0,1] 之间,它表示物体对镜面反射光的反射能力。
- I l I_l Il 是光源的颜色强度。
- R \mathbf{R} R 是反射光线的单位向量,可以通过公式 R = 2 ( N ⋅ L ) N − L \mathbf{R} = 2(\mathbf{N} \cdot \mathbf{L})\mathbf{N} - \mathbf{L} R=2(N⋅L)N−L 计算得到。
- V \mathbf{V} V 是从物体表面指向观察者的单位向量。
- n n n 是高光指数(Shininess),它控制了镜面反射的范围,值越大,高光越集中。
1.3.1. Phong光照模型整体公式
最终的光照颜色 I t o t a l I_{total} Itotal 是环境光、漫反射光和镜面反射光的总和:
I t o t a l = I a m b i e n t + I d i f f u s e + I s p e c u l a r I_{total} = I_{ambient} + I_{diffuse} + I_{specular} Itotal=Iambient+Idiffuse+Ispecular
将前面的公式代入可得:
I t o t a l = k a × I a + k d × I l × max ( 0 , N ⋅ L ) + k s × I l × ( max ( 0 , R ⋅ V ) ) n I_{total} = k_a \times I_a + k_d \times I_l \times \max(0, \mathbf{N} \cdot \mathbf{L}) + k_s \times I_l \times (\max(0, \mathbf{R} \cdot \mathbf{V}))^n Itotal=ka×Ia+kd×Il×max(0,N⋅L)+ks×Il×(max(0,R⋅V))n
上述是一个非常简单的 Phong 光照模型,它只考虑了环境光、漫反射光和镜面反射光三个部分,且漫反射光和镜面反射光均只有一个光源。
如果是有多个光源,则需要分别计算每个光源对物体的光照贡献,然后将它们相加得到最终的光照颜色。
运行结果:
核心代码
//平行光源的漫反射计算vec3 p=ro+rd*d;vec3 n=calcNormal(p);//vec3 lightPosition=vec3(2,2,7);vec3 lightPosition=vec3(-8,-6,-5);//vec3 light_direction=normalize(vec3(1,0,5));vec3 light_direction=normalize(lightPosition-p);vec3 light_color=vec3(1,1,1);//ambientfloat k_a=0.6;vec3 i_a=vec3(0.7,0.7,0);vec3 ambient=k_a*i_a;//diffusefloat k_d=0.5;float dotLN=clamp(dot(n,light_direction),0.0,1.0);vec3 i_d=vec3(0.7,0.5,0.);vec3 diffuse_color=k_d*dotLN*i_d;//specularfloat k_s=0.6;float dotRV=dot(rd,reflect(-light_direction,n));dotRV=clamp(dotRV,0.0,1.0); vec3 i_s=vec3(1,1,1);float alpha=10.;vec3 specular=k_s*pow(dotRV,alpha)*i_s;//c=light_color*diffuse*result.color+backgroundColor*0.2;c=ambient+diffuse_color+ specular;//+ backgroundColor*0.2;//c=diffuse_color*2.;
核心代理中本质就是按照数学公式计算环境光、漫反射光和镜面反射光,然后相加得到最终的光照颜色。
完整代码
#define PIXW (1./iResolution.y)const int MAX_STEPS = 100;
const float START_DIST = 0.001;
const float MAX_DIST = 100.0;
const float EPSILON = 0.0001;struct SDFResult
{float d;vec3 color;
};
mat4 rotationX(float theta)
{return mat4(1.0, 0.0, 0.0, 0.0,0.0, cos(theta), -sin(theta), 0.0,0.0, sin(theta), cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}mat4 rotationY(float theta)
{return mat4(cos(theta), 0.0, sin(theta), 0.0,0.0, 1.0, 0.0, 0.0,-sin(theta), 0.0, cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}
mat4 rotationZ(float theta)
{return mat4(cos(theta), -sin(theta), 0.0, 0.0,sin(theta), cos(theta), 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0);
}SDFResult sdBox( vec3 p, vec3 b,vec3 offset,vec3 color )
{vec3 q = abs(p-offset) - b;return SDFResult(length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0),color);
}vec3 getBackgroundColor(vec2 uv)
{
//uv.y [-1,1]
//y: [0,1] float y=(uv.y+1.)/2.; return mix(vec3(1,0,1),vec3(0,1,1),y);
}SDFResult sdSphere(vec3 p, float r,vec3 offset,vec3 color)
{return SDFResult(length(p-offset)-r,color);
}
SDFResult sdFloor(vec3 p,vec3 color)
{float d=p.y+1.;return SDFResult(d,color);
}SDFResult minWithColor(SDFResult a,SDFResult b)
{if (a.d<b.d){return a;}return b;
}mat3 camera(vec3 cameraPos, vec3 lookAtPoint, vec3 upVector) {vec3 cd = normalize(lookAtPoint - cameraPos); // camera directionvec3 cr = normalize(cross(upVector, cd)); // camera rightvec3 cu = normalize(cross(cd, cr)); // camera upreturn mat3(-cr, cu, -cd); //转换为x轴向右,y轴向上,z轴向屏幕外边的右手坐标系
}SDFResult sdScene(vec3 p)
{SDFResult result1=sdSphere(p,1.,vec3(0,0.,0.),vec3(1,1,1.));//SDFResult result2=sdBox(p,vec3(1.,1.0,1.),vec3(0,0.2,-4),vec3(0.,1.,0.));//SDFResult result3=sdBox(p,vec3(1.,1.0,1.),vec3(4,0.2,-4),vec3(0.,0.,1.));SDFResult result=result1;//minWithColor(result1,result2);//result=minWithColor(result,result3);vec3 floorColor = vec3(1. + 0.7*mod(floor(p.x) + floor(p.z), 2.0));//result=minWithColor(result, sdFloor(p,floorColor));return result;
}
//法线计算
vec3 calcNormal(vec3 p) {vec2 e = vec2(1.0, -1.0) * 0.0005; // epsilonfloat r = 1.; // radius of spherereturn normalize(e.xyy * sdScene(p + e.xyy).d +e.yyx * sdScene(p + e.yyx).d +e.yxy * sdScene(p + e.yxy).d +e.xxx * sdScene(p + e.xxx).d);
}SDFResult rayMarch(vec3 ro, vec3 rd,float start,float end)
{float d=start;float r=1.0;SDFResult result;for(int i=0;i<MAX_STEPS;i++){vec3 p=ro+rd*d;result=sdScene(p);d+=result.d;if(result.d<EPSILON || d>end) break;}result.d=d;return result;
}void mainImage( out vec4 fragColor, in vec2 fragCoord )
{// Normalized pixel coordinates (from -1 to 1)//vec2 uv = (2.0*fragCoord-iResolution.xy)/iResolution.xx;vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;vec3 backgroundColor = vec3(0.835, 1, 1);//vec3 c=getBackgroundColor(uv);vec3 c=backgroundColor;vec3 lp=vec3(0,0.,0.);vec3 ro = vec3(0, 0, 3); // ray origin that represents camera position//float theta=iTime*0.5;//float cameraRadius=10.;//ro.x=cameraRadius*cos(theta)+lp.x;//ro.z=cameraRadius*sin(theta)+lp.z;vec3 rd = camera(ro,lp,vec3(0,1,0))*normalize(vec3(uv, -1)); // ray directionSDFResult result=rayMarch(ro,rd,START_DIST,MAX_DIST);float d=result.d;if(d<MAX_DIST){//平行光源的漫反射计算vec3 p=ro+rd*d;vec3 n=calcNormal(p);//vec3 lightPosition=vec3(2,2,7);vec3 lightPosition=vec3(-8,-6,-5);//vec3 light_direction=normalize(vec3(1,0,5));vec3 light_direction=normalize(lightPosition-p);vec3 light_color=vec3(1,1,1);//ambientfloat k_a=0.6;vec3 i_a=vec3(0.7,0.7,0);vec3 ambient=k_a*i_a;//diffusefloat k_d=0.5;float dotLN=clamp(dot(n,light_direction),0.0,1.0);vec3 i_d=vec3(0.7,0.5,0.);vec3 diffuse_color=k_d*dotLN*i_d;//specularfloat k_s=0.6;float dotRV=dot(rd,reflect(-light_direction,n));dotRV=clamp(dotRV,0.0,1.0); vec3 i_s=vec3(1,1,1);float alpha=10.;vec3 specular=k_s*pow(dotRV,alpha)*i_s;//c=light_color*diffuse*result.color+backgroundColor*0.2;c=ambient+diffuse_color+ specular;//+ backgroundColor*0.2;//c=diffuse_color*2.;}// Output to screenfragColor = vec4(vec3(c),1.0);
}
1.4. 多个光源
对于多个光源,我们只需要将每个光源的贡献相加即可
1.4.1. 一个物体两个光源
下图是一个物体两个光源的贡献
核心代码
- 首先建立一个材质结构体,用来存储材质的属性
struct Material {vec3 ambientColor; // k_a * i_avec3 diffuseColor; // k_d * i_dvec3 specularColor; // k_s * i_sfloat alpha; // shininess
};
- 定义一个金色材质
Material gold()
{return Material(vec3(0.7,0.5,0.)*0.5,vec3(0.7, 0.7, 0.),vec3(1),5.);
}
- 计算漫反射颜色和镜面反射颜色
注意:此处不考虑环境光,环境光是全局的,不需要考虑每个光源
vec3 phongLighting(vec3 lightDir,vec3 normal,vec3 rd,vec3 viewDir,Material material)
{vec3 ambientColor = material.ambientColor;//计算漫反射vec3 diffuse = material.diffuseColor * max(dot(normal, lightDir), 0.0);//计算镜面反射vec3 reflectDir = reflect(-lightDir, normal);float specular = pow(max(dot(reflectDir, viewDir), 0.0), material.alpha);vec3 specularColor = material.specularColor * specular;return ambientColor + diffuse + specularColor;// return diffuse + specularColor;
}
- mainImage函数 (部份)
vec3 lightPosition1=vec3(-8,-6,-5);c=phongLighting(normalize(lightPosition1-p),n,rd,normalize(ro-p),result.mat);
对于每个光源,我们计算出漫反射和镜面反射,然后相加,最后加上环境光即可
完整代码
#define PIXW (1./iResolution.y)const int MAX_STEPS = 100;
const float START_DIST = 0.001;
const float MAX_DIST = 100.0;
const float EPSILON = 0.0001;struct Material {vec3 ambientColor; // k_a * i_avec3 diffuseColor; // k_d * i_dvec3 specularColor; // k_s * i_sfloat alpha; // shininess
};struct SDFResult
{float d;Material mat;
};Material gold()
{return Material(vec3(0.7,0.5,0.)*0.5,vec3(0.7, 0.7, 0.),vec3(1),5.);
}
Material silver()
{return Material(vec3(0.8)*0.4,vec3(0.7)*0.5,vec3(1.)*0.6,5.);
}Material checkerboard(vec3 p)
{return Material(vec3(1. + 0.7*mod(floor(p.x) + floor(p.z), 2.0)) * 0.3,vec3(0.3),vec3(0),1.);
}mat4 rotationX(float theta)
{return mat4(1.0, 0.0, 0.0, 0.0,0.0, cos(theta), -sin(theta), 0.0,0.0, sin(theta), cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}mat4 rotationY(float theta)
{return mat4(cos(theta), 0.0, sin(theta), 0.0,0.0, 1.0, 0.0, 0.0,-sin(theta), 0.0, cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}
mat4 rotationZ(float theta)
{return mat4(cos(theta), -sin(theta), 0.0, 0.0,sin(theta), cos(theta), 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0);
}SDFResult sdBox( vec3 p, vec3 b,vec3 offset,Material mat )
{vec3 q = abs(p-offset) - b;return SDFResult(length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0),mat);
}vec3 getBackgroundColor(vec2 uv)
{
//uv.y [-1,1]
//y: [0,1] float y=(uv.y+1.)/2.; return mix(vec3(1,0,1),vec3(0,1,1),y);
}SDFResult sdSphere(vec3 p, float r,vec3 offset,Material mat)
{return SDFResult(length(p-offset)-r,mat);
}
SDFResult sdFloor(vec3 p,Material mat)
{float d=p.y+1.;return SDFResult(d,mat);
}SDFResult minWithColor(SDFResult a,SDFResult b)
{if (a.d<b.d){return a;}return b;
}mat3 camera(vec3 cameraPos, vec3 lookAtPoint, vec3 upVector) {vec3 cd = normalize(lookAtPoint - cameraPos); // camera directionvec3 cr = normalize(cross(upVector, cd)); // camera rightvec3 cu = normalize(cross(cd, cr)); // camera upreturn mat3(-cr, cu, -cd); //转换为x轴向右,y轴向上,z轴向屏幕外边的右手坐标系
}vec3 phongLighting(vec3 lightDir,vec3 normal,vec3 rd,vec3 viewDir,Material material)
{vec3 ambientColor = material.ambientColor;//计算漫反射vec3 diffuse = material.diffuseColor * max(dot(normal, lightDir), 0.0);//计算镜面反射vec3 reflectDir = reflect(-lightDir, normal);float specular = pow(max(dot(reflectDir, viewDir), 0.0), material.alpha);vec3 specularColor = material.specularColor * specular;return ambientColor + diffuse + specularColor;// return diffuse + specularColor;
}SDFResult sdScene(vec3 p)
{SDFResult result1=sdSphere(p,0.5,vec3(-0.6,0.,0.),gold());SDFResult result2=sdSphere(p,0.5,vec3(0.6,0.,0.),silver());//SDFResult result3=sdBox(p,vec3(1.,1.0,1.),vec3(4,0.2,-4),vec3(0.,0.,1.));SDFResult result=result1;//minWithColor(result1,result2);//result=minWithColor(result,result3);//result=minWithColor(result, sdFloor(p,checkerboard(p)));return result;
}
//法线计算
vec3 calcNormal(vec3 p) {vec2 e = vec2(1.0, -1.0) * 0.0005; // epsilonfloat r = 1.; // radius of spherereturn normalize(e.xyy * sdScene(p + e.xyy).d +e.yyx * sdScene(p + e.yyx).d +e.yxy * sdScene(p + e.yxy).d +e.xxx * sdScene(p + e.xxx).d);
}SDFResult rayMarch(vec3 ro, vec3 rd,float start,float end)
{float d=start;float r=1.0;SDFResult result;for(int i=0;i<MAX_STEPS;i++){vec3 p=ro+rd*d;result=sdScene(p);d+=result.d;if(result.d<EPSILON || d>end) break;}result.d=d;return result;
}void mainImage( out vec4 fragColor, in vec2 fragCoord )
{// Normalized pixel coordinates (from -1 to 1)//vec2 uv = (2.0*fragCoord-iResolution.xy)/iResolution.xx;vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;//vec3 backgroundColor = vec3(0.835, 1, 1);vec3 backgroundColor = mix(vec3(1, .341, .2), vec3(0, 1, 1), uv.y) * 1.6;//vec3 c=getBackgroundColor(uv);vec3 c=backgroundColor;vec3 lp=vec3(0,0.,0.);vec3 ro = vec3(0, 0, 3); // ray origin that represents camera position//float theta=iTime*0.5;//float cameraRadius=10.;//ro.x=cameraRadius*cos(theta)+lp.x;//ro.z=cameraRadius*sin(theta)+lp.z;vec3 rd = camera(ro,lp,vec3(0,1,0))*normalize(vec3(uv, -1)); // ray directionSDFResult result=rayMarch(ro,rd,START_DIST,MAX_DIST);float d=result.d;if(d<MAX_DIST){vec3 p=ro+rd*d;vec3 n=calcNormal(p);//vec3 lightPosition=vec3(2,2,7);vec3 lightPosition1=vec3(-8,-6,-5);c=phongLighting(normalize(lightPosition1-p),n,rd,normalize(ro-p),result.mat);vec3 lightPosition2=vec3(1,1,1);c+=phongLighting(normalize(lightPosition2-p),n,rd,normalize(ro-p),result.mat);}// Output to screenfragColor = vec4(vec3(c),1.0);
}
1.4.2. 多个物体不同材质
我们已经建立了一个物体多个光源的代码,只需略做修改即可有多个物体,每个物体有不同的材质。
核心代码
Material checkerboard(vec3 p)
{return Material(vec3(1. + 0.7*mod(floor(p.x) + floor(p.z), 2.0)) * 0.3,vec3(0.3),vec3(0),1.);
}SDFResult sdFloor(vec3 p,Material mat)
{float d=p.y+1.;return SDFResult(d,mat);
}SDFResult sdScene(vec3 p)
{SDFResult result1=sdSphere(p,0.5,vec3(-0.6,0.,0.),gold());SDFResult result2=sdSphere(p,0.5,vec3(0.6,0.,0.),silver());//SDFResult result3=sdBox(p,vec3(1.,1.0,1.),vec3(4,0.2,-4),vec3(0.,0.,1.));SDFResult result=minWithColor(result1,result2);//result=minWithColor(result,result3);result=minWithColor(result, sdFloor(p,checkerboard(p)));return result;
}
1.5. 轮廓光
先看一个示例:
轮廓光(Rim Light)是一种特殊的光照效果,它在物体边缘处产生亮光,使物体的轮廓更加明显。这种效果在自然界中很常见,比如逆光时物体边缘会出现一圈亮光。
以下是实现轮廓光的主要步骤:
- 原理:
- 轮廓光的强度取决于视线方向(view direction)和法线方向(normal)的夹角
- 当视线方向与法线方向垂直时(即在物体边缘),轮廓光最强
- 当视线方向与法线方向平行时(即在物体正面),轮廓光最弱
// 基本轮廓光计算
float rimLight(vec3 normal, vec3 viewDir) {float rim = 1.0 - max(dot(viewDir, normal), 0.0);return pow(rim, 5.0); // 5.0是强度系数,可以调整
}2. 计算方法:
// 在 phongLighting 函数中添加轮廓光
vec3 phongLighting(vec3 lightDir, vec3 normal, vec3 rd, vec3 viewDir, Material material) {// 原有的环境光、漫反射和镜面反射计算vec3 ambientColor = material.ambientColor;vec3 diffuse = material.diffuseColor * max(dot(normal, lightDir), 0.0);vec3 reflectDir = reflect(-lightDir, normal);float specular = pow(max(dot(reflectDir, viewDir), 0.0), material.alpha);vec3 specularColor = material.specularColor * specular;// 添加轮廓光float rim = rimLight(normal, viewDir);vec3 rimColor = vec3(1.0, 1.0, 1.0) * rim; // 轮廓光颜色return ambientColor + diffuse + specularColor + rimColor;
}
- 效果调整:
- 可以通过调整 pow 函数的指数来控制轮廓光的宽度
- 可以改变轮廓光的颜色
- 可以添加强度系数来控制轮廓光的整体强度
- 常见应用:
- 卡通渲染
- 角色轮廓突出
- 科幻风格效果
- 逆光场景模拟
轮廓光效果可以让3D物体看起来更有层次感,特别适合用于强调物体的形状和边界。在非真实感渲染(NPR)中,轮廓光是一个非常重要的效果。
轮廓光示例
#define PIXW (1./iResolution.y)const int MAX_STEPS = 100;
const float START_DIST = 0.001;
const float MAX_DIST = 100.0;
const float EPSILON = 0.0001;struct SDFResult
{float d;vec3 color;
};
mat4 rotationX(float theta)
{return mat4(1.0, 0.0, 0.0, 0.0,0.0, cos(theta), -sin(theta), 0.0,0.0, sin(theta), cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}mat4 rotationY(float theta)
{return mat4(cos(theta), 0.0, sin(theta), 0.0,0.0, 1.0, 0.0, 0.0,-sin(theta), 0.0, cos(theta), 0.0,0.0, 0.0, 0.0, 1.0);
}
mat4 rotationZ(float theta)
{return mat4(cos(theta), -sin(theta), 0.0, 0.0,sin(theta), cos(theta), 0.0, 0.0,0.0, 0.0, 1.0, 0.0,0.0, 0.0, 0.0, 1.0);
}SDFResult sdBox( vec3 p, vec3 b,vec3 offset,vec3 color )
{vec3 q = abs(p-offset) - b;return SDFResult(length(max(q,0.0)) + min(max(q.x,max(q.y,q.z)),0.0),color);
}vec3 getBackgroundColor(vec2 uv)
{
//uv.y [-1,1]
//y: [0,1] float y=(uv.y+1.)/2.; return mix(vec3(1,0,1),vec3(0,1,1),y);
}SDFResult sdSphere(vec3 p, float r,vec3 offset,vec3 color)
{return SDFResult(length(p-offset)-r,color);
}
SDFResult sdFloor(vec3 p,vec3 color)
{float d=p.y+1.;return SDFResult(d,color);
}SDFResult minWithColor(SDFResult a,SDFResult b)
{if (a.d<b.d){return a;}return b;
}mat3 camera(vec3 cameraPos, vec3 lookAtPoint, vec3 upVector) {vec3 cd = normalize(lookAtPoint - cameraPos); // camera directionvec3 cr = normalize(cross(upVector, cd)); // camera rightvec3 cu = normalize(cross(cd, cr)); // camera upreturn mat3(-cr, cu, -cd); //转换为x轴向右,y轴向上,z轴向屏幕外边的右手坐标系
}SDFResult sdScene(vec3 p)
{SDFResult result1=sdSphere(p,1.,vec3(0,0.,0.),vec3(1,1,1.));//SDFResult result2=sdBox(p,vec3(1.,1.0,1.),vec3(0,0.2,-4),vec3(0.,1.,0.));//SDFResult result3=sdBox(p,vec3(1.,1.0,1.),vec3(4,0.2,-4),vec3(0.,0.,1.));SDFResult result=result1;//minWithColor(result1,result2);//result=minWithColor(result,result3);vec3 floorColor = vec3(1. + 0.7*mod(floor(p.x) + floor(p.z), 2.0));//result=minWithColor(result, sdFloor(p,floorColor));return result;
}
//法线计算
vec3 calcNormal(vec3 p) {vec2 e = vec2(1.0, -1.0) * 0.0005; // epsilonfloat r = 1.; // radius of spherereturn normalize(e.xyy * sdScene(p + e.xyy).d +e.yyx * sdScene(p + e.yyx).d +e.yxy * sdScene(p + e.yxy).d +e.xxx * sdScene(p + e.xxx).d);
}SDFResult rayMarch(vec3 ro, vec3 rd,float start,float end)
{float d=start;float r=1.0;SDFResult result;for(int i=0;i<MAX_STEPS;i++){vec3 p=ro+rd*d;result=sdScene(p);d+=result.d;if(result.d<EPSILON || d>end) break;}result.d=d;return result;
}void mainImage( out vec4 fragColor, in vec2 fragCoord )
{// Normalized pixel coordinates (from -1 to 1)//vec2 uv = (2.0*fragCoord-iResolution.xy)/iResolution.xx;vec2 uv = (fragCoord-0.5*iResolution.xy)/iResolution.y;vec3 backgroundColor = vec3(0.835, 1, 1);//vec3 c=getBackgroundColor(uv);vec3 c=backgroundColor;vec3 lp=vec3(0,0.,0.);vec3 ro = vec3(0, 0, 3); // ray origin that represents camera position//float theta=iTime*0.5;//float cameraRadius=10.;//ro.x=cameraRadius*cos(theta)+lp.x;//ro.z=cameraRadius*sin(theta)+lp.z;vec3 rd = camera(ro,lp,vec3(0,1,0))*normalize(vec3(uv, -1)); // ray directionSDFResult result=rayMarch(ro,rd,START_DIST,MAX_DIST);float d=result.d;if(d<MAX_DIST){//平行光源的漫反射计算vec3 p=ro+rd*d;vec3 n=calcNormal(p);//vec3 lightPosition=vec3(2,2,7);vec3 lightPosition=vec3(-8,-6,-5);//vec3 light_direction=normalize(vec3(1,0,5));vec3 light_direction=normalize(lightPosition-p);vec3 light_color=vec3(1,1,1);//ambientfloat k_a=0.6;vec3 i_a=vec3(0.7,0.7,0);vec3 ambient=k_a*i_a;//diffusefloat k_d=0.5;float dotLN=clamp(dot(n,light_direction),0.0,1.0);vec3 i_d=vec3(0.7,0.5,0.);vec3 diffuse_color=k_d*dotLN*i_d;//specularfloat k_s=0.6;float dotRV=dot(rd,reflect(-light_direction,n));dotRV=clamp(dotRV,0.0,1.0); vec3 i_s=vec3(1,1,1);float alpha=10.;vec3 specular=k_s*pow(dotRV,alpha)*i_s;vec3 toCam=normalize(ro-p);float rimAmt=pow(clamp(1.0-dot(toCam,n),0.0,1.0),5.0);vec3 rimLightColor=vec3(1,1,1)*rimAmt;c=ambient+diffuse_color+ specular +rimLightColor;//c=ambient+diffuse_color+ specular;}// Output to screenfragColor = vec4(vec3(c),1.0);
}
可以通过调整 pow 函数的指数来控制轮廓光的宽度
下图是 指数为 1 的轮廓光效果
下图是 指数为 5 的轮廓光效果
1.6. 参考
- Phong Reflection Model
- GitHub - DragonBaySpark/practical_shader_development: shader开发实战学习笔记
- 11.1 Phong 模型 | Shadertoy中文教程