# 2. 实现过程

## 2.1 光线步进函数

``````const int MAX_STEPS = 100;//最大步进步数
const float MAX_DIST = 100.0;//最大步进距离
const float SURF_DIST = 0.01;//相交检测临近表面距离

float rayMarch(vec3 rayStart, vec3 rayDirection) {
float depth=0.;

for(int i=0; i<MAX_STEPS; i++) {
vec3 p = rayStart + rayDirection*depth;//上一次步进结束后的坐标也就是这一次步进出发点

float dist = getDist(p);//获取当前步进出发点与物体相交时距离
depth += dist; //步进长度累加

if(depth>MAX_DIST || dist<SURF_DIST) break;//步进距离大于最大步进距离或与物体表面距离小于最小表面距离(光线进入物体)停止前进
}
return depth;
}
``````

## 2.2 获取物体表面距离

``````float sdSphere( vec3 p, float s )
{
return length(p)-s;
}

float getDist(vec3 p){

vec3 spherrCenter = vec3(0,1,5);
float plane = p.y;//地面
return min(dist,plane);// min 函数求并集然后返回
}
``````

## 2.3 计算光照阴影

``````//计算表面法线
vec3 getNormal(vec3 p){
return normalize(vec3(
getDist(vec3(p.x + SURF_DIST, p.y, p.z)) - getDist(vec3(p.x - SURF_DIST, p.y, p.z)),
getDist(vec3(p.x, p.y + SURF_DIST, p.z)) - getDist(vec3(p.x, p.y - SURF_DIST, p.z)),
getDist(vec3(p.x, p.y, p.z  + SURF_DIST)) - getDist(vec3(p.x, p.y, p.z - SURF_DIST))
));

}

float getLight(vec3 p){
vec3 lightPos = vec3(0,5,7);
lightPos.xz += vec2(sin(u_time),cos(u_time));
vec3 light = normalize(lightPos-p);
vec3 normal = getNormal(p);

float diffuse = clamp(dot(normal,light),0.0,1.0);//限制在0~1

//计算阴影
float d = rayMarch(p + normal*SURF_DIST*2.0,light);
if(d<length(lightPos-p)){
diffuse*=0.1;
}
return diffuse;
}
``````

## 2.4 物体表面距离计算

``````void main( void ) {

//窗口坐标调整为[-1,1],坐标原点在屏幕中心
vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;

vec3 color = vec3(0.6);//背景色

vec3 ro = vec3(0.0,1.0,0.0);//视点
vec3 rd = normalize(vec3(st.x,st.y,1.0));//光线方向

float d = rayMarch(ro,rd);

vec3 p = ro + rd * d;
float diffuse = getLight(p);
color = vec3(diffuse);

gl_FragColor = vec4(color, 1.0);

}
``````

# 4 demo代码

``````<body>
<div id="container"></div>
<script src="http://www.yanhuangxueyuan.com/versions/threejsR92/build/three.js"></script>
<script>
var container;
var camera, scene, renderer;
var uniforms;
void main() {
gl_Position = vec4( position, 1.0 );
}
`
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
uniform vec2 u_mouse;
uniform vec2 u_resolution;

const int MAX_STEPS = 100;//最大步进步数
const float MAX_DIST = 100.0;//最大步进距离
const float SURF_DIST = 0.01;//相交检测临近表面距离

float sdSphere( vec3 p, float s )
{
return length(p)-s;
}

float getDist(vec3 p){

vec3 spherrCenter = vec3(0,1,5);
float plane = p.y;//地面
return min(dist,plane);// min 函数求并集然后返回
}

float rayMarch(vec3 rayStart, vec3 rayDirection) {
float depth=0.;

for(int i=0; i<MAX_STEPS; i++) {
vec3 p = rayStart + rayDirection*depth;//上一次步进结束后的坐标也就是这一次步进出发点

float dist = getDist(p);//获取当前步进出发点与物体相交时距离
depth += dist; //步进长度累加

if(depth>MAX_DIST || dist<SURF_DIST) break;//步进距离大于最大步进距离或与物体表面距离小于最小表面距离(光线进入物体)停止前进
}
return depth;
}

vec3 getNormal(vec3 p){
return normalize(vec3(
getDist(vec3(p.x + SURF_DIST, p.y, p.z)) - getDist(vec3(p.x - SURF_DIST, p.y, p.z)),
getDist(vec3(p.x, p.y + SURF_DIST, p.z)) - getDist(vec3(p.x, p.y - SURF_DIST, p.z)),
getDist(vec3(p.x, p.y, p.z  + SURF_DIST)) - getDist(vec3(p.x, p.y, p.z - SURF_DIST))
));

}

float getLight(vec3 p){
vec3 lightPos = vec3(0,5,7);
lightPos.xz += vec2(sin(u_time),cos(u_time));
vec3 light = normalize(lightPos-p);
vec3 normal = getNormal(p);

float diffuse = clamp(dot(normal,light),0.0,1.0);//限制在0~1

//计算阴影
float d = rayMarch(p + normal*SURF_DIST*2.0,light);
if(d<length(lightPos-p)){
diffuse*=0.1;
}
return diffuse;
}

void main( void ) {

//窗口坐标调整为[-1,1],坐标原点在屏幕中心
vec2 st = (gl_FragCoord.xy * 2. - u_resolution) / u_resolution.y;

vec3 color = vec3(0.6);//背景色

vec3 ro = vec3(0.0,1.0,0.0);//视点
vec3 rd = normalize(vec3(st.x,st.y,1.0));//视线方向

float d = rayMarch(ro,rd);

vec3 p = ro + rd * d;
float diffuse = getLight(p);//漫反射光计算
color = vec3(diffuse);

gl_FragColor = vec4(color, 1.0);

}
`

init();
animate();

function init() {
container = document.getElementById('container');

camera = new THREE.Camera();
camera.position.z = 1;

scene = new THREE.Scene();

var geometry = new THREE.PlaneBufferGeometry(2, 2);

uniforms = {
u_time: {
type: "f",
value: 1.0
},
u_resolution: {
type: "v2",
value: new THREE.Vector2()
},
u_mouse: {
type: "v2",
value: new THREE.Vector2()
}
};

uniforms: uniforms,
});

var mesh = new THREE.Mesh(geometry, material);

renderer = new THREE.WebGLRenderer();
//renderer.setPixelRatio(window.devicePixelRatio);

container.appendChild(renderer.domElement);

onWindowResize();

document.onmousemove = function (e) {
uniforms.u_mouse.value.x = e.pageX
uniforms.u_mouse.value.y = e.pageY
}
}

function onWindowResize(event) {
renderer.setSize(800, 800);
uniforms.u_resolution.value.x = renderer.domElement.width;
uniforms.u_resolution.value.y = renderer.domElement.height;
}

function animate() {
requestAnimationFrame(animate);
render();
}

function render() {
uniforms.u_time.value += 0.02;
renderer.render(scene, camera);
}
</script>
</body>
``````