# 2. 实现过程

## 2.1 模型的旋转操作

``````//绕z轴旋转矩阵
mat4 rotZ(float a) {
return mat4(cos(a),-sin(a),0.0,0.0,
sin(a),cos(a),0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0
);
}

//绕x轴旋转矩阵
mat4 rotX(float a) {
return mat4(1.0,0.0,0.0,0.0,
0.0,cos(a),-sin(a),0.0,
0.0,sin(a),cos(a),0.0,
0.0,0.0,0.0,1.0
);
}

//绕y轴旋转矩阵
mat4 rotY(float a) {
return mat4(cos(a),0.0,sin(a),0.0,
0.0,1.0,0.0,0.0,
-sin(a),0.0,cos(a),0.0,
0.0,0.0,0.0,1.0
);
}
``````

``````vec2 getDistandMaterial(vec3 p){

float plane = p.y;//地面

vec2 res = vec2(plane,0.0);

//未变换前的立方体
vec2 box0 = vec2(sdBox(p-vec3(-1.5,2,5),vec3(0.6),0.06),1.0);//原方块,材质ID为1.0

vec3 pos = p-vec3(1.5,2,5);//确定模型的中心
vec4 boxPos = vec4(pos,1.0);//转为其次坐标

boxPos*=rotX(u_time);
boxPos*=rotY(u_time);
boxPos*=rotZ(u_time);
vec2 box = vec2(sdBox(boxPos.xyz,vec3(0.6),0.06),2.0);//变换方块,材质ID为2.0

res = opU(res,box0);
res = opU(res,box);

return res;
}
``````

## 2.2 模型的缩放操作

``````//缩放矩阵
mat4 scaled(vec3 scale){
//缩放矩阵作用于坐标系，所以放大和缩小刚好相反，为了使用习惯，缩放参数取倒数
return mat4(1.0/scale.x,0.0,0.0,0.0,
0.0,1.0/scale.y,0.0,0.0,
0.0,0.0,1.0/scale.z,0.0,
0.0,0.0,0.0,1.0
);
}
``````

``````vec3 scale = vec3(1.2,1.5,2.0);//确定缩放倍数
boxPos *= scaled(scale);
vec2 box = vec2(sdBox(boxPos.xyz,vec3(0.6),0.06),2.0);//变换方块,材质ID为2.0
``````

``````boxPos.x *= 2.0;//x轴方向缩小两倍
boxPos.y /= 1.5;//y轴方向放大1.5倍
``````

## 2.3 模型的平移操作

``````//平移矩阵
mat4 translation3d(vec3 translate){
//平移矩阵作用于坐标系，所以平移的方向相反，为了使用习惯，缩放参数取负
return mat4(1.0,0.0,0.0,-translate.x,
0.0,1.0,0.0,-translate.y,
0.0,0.0,1.0,-translate.z,
0.0,0.0,0.0,1.0
);
}
``````

``````pos = p-vec3(-1.5,2,5);//平移前将橙色方块移到与蓝色方块重合位置
boxPos = vec4(pos,1.0);//转为其次坐标
vec3 trans = vec3(2.0,1.8,0.2);//确定沿xyz轴的平移距离
boxPos *= translation3d(trans);

vec2 box = vec2(sdBox(boxPos.xyz,vec3(0.6),0.06),2.0);//变换方块,材质ID为2.0
``````

``````boxPos.x -= 2.0;//右移2个单位
boxPos.y -= 1.8;//上移1.8个单
``````

# 3. 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;//相交检测临近表面距离

//绕z轴旋转矩阵
mat4 rotZ(float a) {
return mat4(cos(a),-sin(a),0.0,0.0,
sin(a),cos(a),0.0,0.0,
0.0,0.0,1.0,0.0,
0.0,0.0,0.0,1.0
);
}

//绕x轴旋转矩阵
mat4 rotX(float a) {
return mat4(1.0,0.0,0.0,0.0,
0.0,cos(a),-sin(a),0.0,
0.0,sin(a),cos(a),0.0,
0.0,0.0,0.0,1.0
);
}

//绕y轴旋转矩阵
mat4 rotY(float a) {
return mat4(cos(a),0.0,sin(a),0.0,
0.0,1.0,0.0,0.0,
-sin(a),0.0,cos(a),0.0,
0.0,0.0,0.0,1.0
);
}

//缩放矩阵
mat4 scaled(vec3 scale){
//缩放矩阵作用于坐标系，所以放大和缩小刚好相反，为了使用习惯，缩放参数取倒数
return mat4(1.0/scale.x,0.0,0.0,0.0,
0.0,1.0/scale.y,0.0,0.0,
0.0,0.0,1.0/scale.z,0.0,
0.0,0.0,0.0,1.0
);
}

//平移矩阵
mat4 translation3d(vec3 translate){
//平移矩阵作用于坐标系，所以平移的方向相反，为了使用习惯，缩放参数取负
return mat4(1.0,0.0,0.0,-translate.x,
0.0,1.0,0.0,-translate.y,
0.0,0.0,1.0,-translate.z,
0.0,0.0,0.0,1.0
);
}

vec2 opU( vec2 d1, vec2 d2 )
{
return (d1.x<d2.x) ? d1 : d2;
}

//立方体
float sdBox( vec3 p, vec3 b,float rad )
{
vec3 d = abs(p) - b;
return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)) - rad;
}

vec2 getDistandMaterial(vec3 p){

float plane = p.y;//地面

vec2 res = vec2(plane,0.0);

//未变换前的立方体
vec2 box0 = vec2(sdBox(p-vec3(-1.5,2,5),vec3(0.6),0.06),1.0);//原方块,材质ID为1.0

vec3 pos = p-vec3(1.5,2,5);//确定模型的中心
vec4 boxPos = vec4(pos,1.0);//转为其次坐标

//  boxPos*=rotX(u_time);
//  boxPos*=rotY(u_time);
//  boxPos*=rotZ(u_time);

// vec3 scale = vec3(1.2,1.5,2.0);//确定缩放倍数
// boxPos *= scaled(scale);

pos = p-vec3(-1.5,2,5);//平移前将橙色方块移到与蓝色方块重合位置
boxPos = vec4(pos,1.0);//转为其次坐标
vec3 trans = vec3(2.0,1.8,0.2);//确定沿xyz轴的平移距离
//boxPos *= translation3d(trans);

//缩放的另一种方式
boxPos.x *= 2.0;//x轴方向缩小两倍
boxPos.y /= 1.5;//y轴方向放大1.5倍

//平移的另一种方式
boxPos.x -= 2.0;//右移2个单位
boxPos.y -= 1.8;//上移1.8个单

vec2 box = vec2(sdBox(boxPos.xyz,vec3(0.6),0.06),2.0);//变换方块,材质ID为2.0

res = opU(res,box0);
res = opU(res,box);

return res;
}

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

vec2 dm = getDistandMaterial(p);
float dist = dm.x;//获取当前步进出发点与物体相交时距离
material = dm.y;
depth += dist; //步进长度累加

// if(depth>MAX_DIST){
//   material = -1.0;
// }

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

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

//Blinn-Phong模型光照计算
vec3 calcBlinnPhongLight( vec3 materialColor, vec3 p, vec3 ro) {

vec3 lightPos = vec3(5.0 * sin(u_time), 20.0, 10.0*cos(u_time)-18.);//光源坐标

//计算环境光
float k_a = 0.2;//环境光反射系数
vec3 ambientLight = 0.6 * vec3(1.0, 1.0, 1.0);
vec3 ambient = k_a*ambientLight;

vec3 N = getNormal(p); //法线
vec3 L = normalize(lightPos - p); //光照方向
vec3 V = normalize(ro - p); //视线
vec3 H = normalize(V+L); //半程向量

float r = length(lightPos - p);

//计算漫反射光
float k_d = 0.6;//漫反射系数
float dotLN = clamp(dot(L, N),0.0,1.0);//点乘，并将结果限定在0~1
vec3 diffuse = k_d * (materialColor/r*r) * dotLN;

//计算高光反射光
float k_s = 0.8;//镜面反射系数
float shininess = 160.0;
vec3 specularColor = vec3(1.0, 1.0, 1.0);
vec3 specular = k_s * (specularColor/r*r)* pow(clamp(dot(N, H), 0.0, 1.0), shininess);//计算高光

//计算阴影
vec2 res = rayMarch(p + N*SURF_DIST*2.0,L);
if(res.x<length(lightPos-p)-0.001){
diffuse*=0.1;
}

//颜色 =  环境光 + 漫反射光 + 镜面反射光
return ambient +diffuse + specular;
}

void main( void ) {

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

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

vec2 res = rayMarch(ro,rd);//反向光线追踪求交点距离与材质ID

float d = res.x;//物体与视点的距离
float m = res.y;//材质ID

vec3 p = ro + rd * d;

vec3 materialColor = vec3(1.0, 1.0, 1.0);

//为不同物体设置不同的材质颜色
if(m==0.0){
materialColor = vec3(.2, 0.0, 0.0);
}
if(m==1.0){
materialColor = vec3(.2, 0.0, 1.0);
}
if(m==2.0){
materialColor = vec3(.7, 0.2, 0.0);
}

vec3 color = vec3(1.0,1.0,1.0);

//使用Blinn-Phong模型计算光照
color *= calcBlinnPhongLight( materialColor, p, ro);

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>
``````