<canvas id="nes_screen" width="200" height="120">
</canvas>
<script>
var iGlobalTime = 0.0;
var iGlobalStartTime = 0.0;
// 向量
var vec2 = function (x, y) {
this.x = x;
this.y = y;
}
vec2.prototype = {
Length: function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
,
Add: function (other) {
return new vec2(this.x + other.x, this.y + other.y);
}
,
Sub: function (other) {
return new vec2(this.x - other.x, this.y - other.y);
}
,
Mul: function (scale) {
return new vec2(this.x * scale, this.y * scale);
}
,
Div: function (scale) {
return new vec2(this.x / scale, this.y / scale);
}
,
Dot: function (other) {
return this.x * other.x + this.y * other.y;
}
}
vec2.zore = new vec2(0.0, 0.0);
var vec3 = function (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
vec3.prototype = {
Length: function () {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
}
,
Add: function (other) {
return new vec3(this.x + other.x, this.y + other.y, this.y + other.y);
}
,
Sub: function (other) {
return new vec3(this.x - other.x, this.y - other.y, this.z - other.z);
}
,
Mul: function (scale) {
return new vec3(this.x * scale, this.y * scale, this.z * scale);
}
,
Div: function (scale) {
return new vec3(this.x / scale, this.y / scale, this.z / scale);
}
,
Dot: function (other) {
return this.x * other.x + this.y * other.y + this.z * other.z;
}
,
Cross: function (other) {
return new vec3(this.y * other.z - this.z * other.y,
this.z * other.x - this.x * other.z,
this.x * other.y - this.y * other.x);
}
,
Normalize: function () {
var len = this.Length();
if (len > 0.0) {
var invLen = 1 / len;
return new vec3(this.x * invLen, this.y * invLen, this.z * invLen)
}
return new vec3(0.0, 0.0, 0.0);
}
}
vec3.zore = new vec3(0.0, 0.0, 0.0);
function radians(angle) {
return (Math.PI / 180.0) * angle;
}
function cos(delta) {
return Math.cos(delta);
}
function sin(delta) {
return Math.sin(delta);
}
function clamp(v, min, max) {
return (v > max) ? max : ((v < min) ? min : v);
}
// 颜色
var Color = function (r, g, b, a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
Color.prototype = {
Add: function (oterh) {
this.r += oterh.r;
this.g += oterh.g;
this.b += oterh.b;
this.a += oterh.a;
return this;
}
,
Sub: function (oterh) {
this.r -= oterh.r;
this.g -= oterh.g;
this.b -= oterh.b;
this.a -= oterh.a;
return this;
}
,
Mul: function (scale) {
this.r *= scale;
this.g *= scale;
this.b *= scale;
this.a *= scale;
return this;
}
,
Div: function (oterh) {
this.r /= scale;
this.g /= scale;
this.b /= scale;
this.a /= scale;
return this;
}
}
Color.balck = new Color(0.0, 0.0, 0.0, 255.0);
Color.white = new Color(255.0, 255.0, 255.0, 255.0);
var mat4 = function (m11, m12, m13, m14,
m21, m22, m23, m24,
m31, m32, m33, m34,
m41, m42, m43, m44) {
this.m11 = m11; this.m12 = m12; this.m13 = m13; this.m14 = m14;
this.m21 = m21; this.m22 = m22; this.m23 = m23; this.m24 = m24;
this.m31 = m31; this.m32 = m32; this.m33 = m33; this.m34 = m34;
this.m41 = m41; this.m42 = m42; this.m43 = m43; this.m44 = m44;
}
mat4.prototype = {
Mul: function (other) {
var new_m11 = other.m11 * this.m11 + other.m12 * this.m21 + other.m13 * this.m31 + other.m14 * this.m41;
var new_m12 = other.m11 * this.m12 + other.m12 * this.m22 + other.m13 * this.m32 + other.m14 * this.m42;
var new_m13 = other.m11 * this.m13 + other.m12 * this.m23 + other.m13 * this.m33 + other.m14 * this.m43;
var new_m14 = other.m11 * this.m14 + other.m12 * this.m24 + other.m13 * this.m34 + other.m14 * this.m44;
var new_m21 = other.m21 * this.m11 + other.m22 * this.m21 + other.m23 * this.m31 + other.m24 * this.m41;
var new_m22 = other.m21 * this.m12 + other.m22 * this.m22 + other.m23 * this.m32 + other.m24 * this.m42;
var new_m23 = other.m21 * this.m13 + other.m22 * this.m23 + other.m23 * this.m33 + other.m24 * this.m43;
var new_m24 = other.m21 * this.m14 + other.m22 * this.m24 + other.m23 * this.m34 + other.m24 * this.m44;
var new_m31 = other.m31 * this.m11 + other.m32 * this.m21 + other.m33 * this.m31 + other.m34 * this.m41;
var new_m32 = other.m31 * this.m12 + other.m32 * this.m22 + other.m33 * this.m32 + other.m34 * this.m42;
var new_m33 = other.m31 * this.m13 + other.m32 * this.m23 + other.m33 * this.m33 + other.m34 * this.m43;
var new_m34 = other.m31 * this.m14 + other.m32 * this.m24 + other.m33 * this.m34 + other.m34 * this.m44;
var new_m41 = other.m41 * this.m11 + other.m42 * this.m21 + other.m43 * this.m31 + other.m44 * this.m41;
var new_m42 = other.m41 * this.m12 + other.m42 * this.m22 + other.m43 * this.m32 + other.m44 * this.m42;
var new_m43 = other.m41 * this.m13 + other.m42 * this.m23 + other.m43 * this.m33 + other.m44 * this.m43;
var new_m44 = other.m41 * this.m14 + other.m42 * this.m24 + other.m43 * this.m34 + other.m44 * this.m44;
return new mat4(new_m11, new_m12, new_m13, new_m14,
new_m21, new_m22, new_m23, new_m24,
new_m31, new_m32, new_m33, new_m34,
new_m41, new_m42, new_m43, new_m44);
},
MulVec3: function (vec3_a) {
var new_x = vec3_a.x * this.m11 + vec3_a.y * this.m21 + vec3_a.z * this.m31 + 1.0 * this.m41;
var new_y = vec3_a.x * this.m12 + vec3_a.y * this.m22 + vec3_a.z * this.m32 + 1.0 * this.m42;
var new_z = vec3_a.x * this.m13 + vec3_a.y * this.m23 + vec3_a.z * this.m33 + 1.0 * this.m43;
return new vec3(new_x, new_y, new_z);
}
,
MulNormal: function (vec3_a) {
var new_x = vec3_a.x * this.m11 + vec3_a.y * this.m21 + vec3_a.z * this.m31;
var new_y = vec3_a.x * this.m12 + vec3_a.y * this.m22 + vec3_a.z * this.m32;
var new_z = vec3_a.x * this.m13 + vec3_a.y * this.m23 + vec3_a.z * this.m33;
return new vec3(new_x, new_y, new_z);
}
}
function setRotation(float_x, float_y, float_z) {
var a = sin(float_x); var b = cos(float_x);
var c = sin(float_y); var d = cos(float_y);
var e = sin(float_z); var f = cos(float_z);
var ac = a * c;
var bc = b * c;
return new mat4(d * f, d * e, -c, 0.0,
ac * f - b * e, ac * e + b * f, a * d, 0.0,
bc * f + a * e, bc * e - a * f, b * d, 0.0,
0.0, 0.0, 0.0, 1.0);
}
function setTranslation(float_x, float_y, float_z) {
return new mat4(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
float_x, float_y, float_z, 1.0);
}
var Triangle = function () {
this.vec3_a = vec3.zore; this.vec2_aUV = vec2.zore;
this.vec3_b = vec3.zore; this.vec2_bUV = vec2.zore;
this.vec3_c = vec3.zore; this.vec2_cUV = vec2.zore;
this.vec3_n = vec3.zore;
};
triangles = new Array();
triangles[0] = new Triangle();
triangles[1] = new Triangle();
triangles[2] = new Triangle();
triangles[3] = new Triangle();
triangles[4] = new Triangle();
triangles[5] = new Triangle();
triangles[6] = new Triangle();
triangles[7] = new Triangle();
triangles[8] = new Triangle();
triangles[9] = new Triangle();
triangles[10] = new Triangle();
triangles[11] = new Triangle();
triangles[12] = new Triangle();
triangles[13] = new Triangle();
function createCube() {
verts = new Array();
verts[0] = new vec3(-1.0, -1.0, -1.0);
verts[1] = new vec3(-1.0, -1.0, 1.0);
verts[2] = new vec3(-1.0, 1.0, -1.0);
verts[3] = new vec3(-1.0, 1.0, 1.0);
verts[4] = new vec3(1.0, -1.0, -1.0);
verts[5] = new vec3(1.0, -1.0, 1.0);
verts[6] = new vec3(1.0, 1.0, -1.0);
verts[7] = new vec3(1.0, 1.0, 1.0);
triangles[0].vec3_a = verts[1]; triangles[0].vec2_aUV = new vec2(0.0, 0.0);
triangles[0].vec3_b = verts[5]; triangles[0].vec2_bUV = new vec2(1.0, 0.0);
triangles[0].vec3_c = verts[7]; triangles[0].vec2_cUV = new vec2(1.0, 1.0);
triangles[0].vec3_n = new vec3(0.0, 0.0, 1.0);
triangles[1].vec3_a = verts[1]; triangles[1].vec2_aUV = new vec2(0.0, 0.0);
triangles[1].vec3_b = verts[7]; triangles[1].vec2_bUV = new vec2(1.0, 1.0);
triangles[1].vec3_c = verts[3]; triangles[1].vec2_cUV = new vec2(0.0, 1.0);
triangles[1].vec3_n = new vec3(0.0, 0.0, 1.0);
triangles[2].vec3_a = verts[5]; triangles[2].vec2_aUV = new vec2(0.0, 0.0);
triangles[2].vec3_b = verts[4]; triangles[2].vec2_bUV = new vec2(1.0, 0.0);
triangles[2].vec3_c = verts[6]; triangles[2].vec2_cUV = new vec2(1.0, 1.0);
triangles[2].vec3_n = new vec3(1.0, 0.0, 0.0);
triangles[3].vec3_a = verts[5]; triangles[3].vec2_aUV = new vec2(0.0, 0.0);
triangles[3].vec3_b = verts[6]; triangles[3].vec2_bUV = new vec2(1.0, 1.0);
triangles[3].vec3_c = verts[7]; triangles[3].vec2_cUV = new vec2(0.0, 1.0);
triangles[3].vec3_n = new vec3(1.0, 0.0, 0.0);
triangles[4].vec3_a = verts[3]; triangles[4].vec2_aUV = new vec2(0.0, 0.0);
triangles[4].vec3_b = verts[7]; triangles[4].vec2_bUV = new vec2(1.0, 0.0);
triangles[4].vec3_c = verts[6];; triangles[4].vec2_cUV = new vec2(1.0, 1.0);
triangles[4].vec3_n = new vec3(0.0, 1.0, 0.0);
triangles[5].vec3_a = verts[3]; triangles[5].vec2_aUV = new vec2(0.0, 0.0);
triangles[5].vec3_b = verts[6]; triangles[5].vec2_bUV = new vec2(1.0, 1.0);
triangles[5].vec3_c = verts[2]; triangles[5].vec2_cUV = new vec2(0.0, 1.0);
triangles[5].vec3_n = new vec3(0.0, 1.0, 0.0);
triangles[6].vec3_a = verts[0]; triangles[6].vec2_aUV = new vec2(1.0, 0.0);
triangles[6].vec3_b = verts[6]; triangles[6].vec2_bUV = new vec2(0.0, 1.0);
triangles[6].vec3_c = verts[4]; triangles[6].vec2_cUV = new vec2(0.0, 0.0);
triangles[6].vec3_n = new vec3(0.0, 0.0, -1.0);
triangles[7].vec3_a = verts[0]; triangles[7].vec2_aUV = new vec2(1.0, 0.0);
triangles[7].vec3_b = verts[2]; triangles[7].vec2_bUV = new vec2(1.0, 1.0);
triangles[7].vec3_c = verts[6]; triangles[7].vec2_cUV = new vec2(0.0, 1.0);
triangles[7].vec3_n = new vec3(0.0, 0.0, -1.0);
triangles[8].vec3_a = verts[1]; triangles[8].vec2_aUV = new vec2(1.0, 0.0);
triangles[8].vec3_b = verts[2]; triangles[8].vec2_bUV = new vec2(0.0, 1.0);
triangles[8].vec3_c = verts[0]; triangles[8].vec2_cUV = new vec2(0.0, 0.0);
triangles[8].vec3_n = new vec3(-1.0, 0.0, 0.0);
triangles[9].vec3_a = verts[1]; triangles[9].vec2_aUV = new vec2(1.0, 0.0);
triangles[9].vec3_b = verts[3]; triangles[9].vec2_bUV = new vec2(1.0, 1.0);
triangles[9].vec3_c = verts[2]; triangles[9].vec2_cUV = new vec2(0.0, 1.0);
triangles[9].vec3_n = new vec3(-1.0, 0.0, 0.0);
triangles[10].vec3_a = verts[1]; triangles[10].vec2_aUV = new vec2(0.0, 0.0);
triangles[10].vec3_b = verts[0]; triangles[10].vec2_bUV = new vec2(0.0, 1.0);
triangles[10].vec3_c = verts[4]; triangles[10].vec2_cUV = new vec2(1.0, 1.0);
triangles[10].vec3_n = new vec3(0.0, -1.0, 0.0);
triangles[11].vec3_a = verts[1]; triangles[11].vec2_aUV = new vec2(0.0, 0.0);
triangles[11].vec3_b = verts[4]; triangles[11].vec2_bUV = new vec2(1.0, 1.0);
triangles[11].vec3_c = verts[5]; triangles[11].vec2_cUV = new vec2(1.0, 0.0);
triangles[11].vec3_n = new vec3(0.0, -1.0, 0.0);
}
function createFloor() {
verts = new Array();
verts[0] = new vec3(-3.0, -1.0, -3.0);
verts[1] = new vec3(-3.0, -1.0, 3.0);
verts[2] = new vec3(3.0, -1.0, 3.0);
verts[3] = new vec3(3.0, -1.0, -3.0);
triangles[12].vec3_a = verts[0]; triangles[12].vec2_aUV = new vec2(0.0, 0.0);
triangles[12].vec3_b = verts[1]; triangles[12].vec2_bUV = new vec2(0.0, 3.0);
triangles[12].vec3_c = verts[3]; triangles[12].vec2_cUV = new vec2(3.0, 0.0);
triangles[12].vec3_n = new vec3(0.0, 1.0, 0.0);
triangles[13].vec3_a = verts[1]; triangles[13].vec2_aUV = new vec2(0.0, 3.0);
triangles[13].vec3_b = verts[2]; triangles[13].vec2_bUV = new vec2(3.0, 3.0);
triangles[13].vec3_c = verts[3]; triangles[13].vec2_cUV = new vec2(3.0, 0.0);
triangles[13].vec3_n = new vec3(0.0, 1.0, 0.0);
}
function RayIntersectTriangle(
vec3_ro, vec3_rd,
vec3_v0, vec3_v1, vec3_v2,
vec3_result) {
var E1 = vec3_v1.Sub(vec3_v0);
var E2 = vec3_v2.Sub(vec3_v0);
var P = vec3_rd.Cross(E2);
var det = E1.Dot(P);
var T = vec3.zore;
if (det > 0.0)
T = vec3_ro.Sub(vec3_v0);
else {
T = vec3_v0.Sub(vec3_ro);
det = -det;
}
if (det < 0.001)
return false;
var u = T.Dot(P);
if (u < 0. || u > det)
return false;
var Q = T.Cross(E1);
v = vec3_rd.Dot(Q);
if (v < 0. || u + v > det)
return false;
vec3_result.x = E2.Dot(Q);
var fInvDet = 1. / det;
vec3_result.x *= fInvDet;
vec3_result.y *= fInvDet;
vec3_result.z *= fInvDet;
return true;
}
var mdv;
var lig = new vec3(0.3, 0.7, 0.5).Normalize();
function SurfaceShader(vec3_nor, vec2_uv, float_z, vec3_wnor) {
var dif = clamp(vec3_nor.Dot(lig), 0.0, 1.0);
var brdf = 0.5 + 0.8 * dif;
brdf *= 4.0 * Math.exp(-0.7 * Math.abs(float_z));
return new Color(128.0 * brdf, 128.0 * brdf, 196.0 * brdf, 255);
}
function pixel_shader(x, y, w, h) {
var pix = new vec2(-1.0 + 2.0 * x / w, -1.0 + 2.0 * y / h);
pix.x *= w / h;
var ro = new vec3(0.0, 0.0, 2.0);
var rd = new vec3(pix.x, pix.y, 0.0).Sub(ro).Normalize();
var color = Color.balck;
// clear zbuffer
var mindist = -1000000.0;
for (var i = 0; i < 14; i++) {
// transform to eye space
var ep0 = mdv.MulVec3(triangles[i].vec3_a);
var ep1 = mdv.MulVec3(triangles[i].vec3_b);
var ep2 = mdv.MulVec3(triangles[i].vec3_c);
var nor = mdv.MulNormal(triangles[i].vec3_n);
var result = vec3.zore;
if (RayIntersectTriangle(ro, rd, ep0, ep1, ep2, result)) {
// do uv linear interpolation.
var uv = triangles[i].vec2_bUV.Sub(triangles[i].vec2_aUV).Mul(result.y) +
triangles[i].vec2_cUV.Sub(triangles[i].vec2_aUV).Mul(result.z);
var z = ro.z + result.x * rd.z;
if (z > mindist) {
mindist = z;
// perform lighting/shading
color = SurfaceShader(nor, uv, z, triangles[i].n);
}
}
}
return color;
}
function main() {
var d = new Date();
iGlobalTime = (d.getTime() - iGlobalStartTime) / 1000;
mdv = setTranslation(0.0, 0.5, -3.0).Mul(
setRotation(0.6, 0.0, 0.0).Mul(
setRotation(0.0, 3.1 * sin(0.3 * iGlobalTime), 0.0)
)
);
var canvas = document.getElementById('nes_screen');
if (canvas && canvas.getContext) {
var ctx = canvas.getContext('2d');
var w = canvas.clientWidth, h = canvas.clientHeight;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.fillRect(0, 0, w, h);
var imgdata = ctx.getImageData(0, 0, w, h);
var pixels = imgdata.data;
var i = 0;
for (var y = h - 1; y >= 0; y--)
for (var x = 0; x < w; x++) {
var data = pixel_shader(x, y, w, h);
pixels[i++] = data.r;
pixels[i++] = data.g;
pixels[i++] = data.b;
pixels[i++] = data.a;
}
ctx.putImageData(imgdata, 0, 0);
}
//setTimeout(main,33);
requestAnimationFrame(main);
}
try {
var d = new Date();
iGlobalStartTime = d.getTime();
createCube();
createFloor();
main();
}
catch (err) {
alert(err.message);
}
</script>