public static Vector3Byte Lerp(Vector3Byte a, Vector3Byte b, float t) { t = Helpers.Clamp(t, 0f, 1f); byte xResult = (byte)(a.x * t + b.x * (1 - t)); byte yResult = (byte)(a.y * t + b.y * (1 - t)); byte zResult = (byte)(a.z * t + b.z * (1 - t)); return(new Vector3Byte(xResult, yResult, zResult)); }
public SphereElement(float x, float y, float z, float r, float screenZ) { this.x = x; this.y = y; this.z = z; this.r = r; this.screenZ = screenZ; colorA = new Vector3Byte(Helpers.RandomByte(), Helpers.RandomByte(), Helpers.RandomByte()); colorB = new Vector3Byte(Helpers.RandomByte(), Helpers.RandomByte(), Helpers.RandomByte()); }
public byte Dot(Vector3Byte v2) { return((byte)(x * v2.x + y * v2.y + z * v2.z)); }
private void RenderSphere(SphereElement sphere, float rotationSin, float rotationCos, float colorLerpProgress, Vector3 lightDir) { float fX = sphere.x * rotationSin - sphere.z * rotationCos; float fY = sphere.y; float fZ = sphere.screenZ; fZ += 1.5f; //Skip the sphere that's too close to camera frustum if (fZ < 0.001f) { return; } //Weak perspective projection float screenX = fX / fZ; float screenY = fY / fZ; float screenZ = fZ; float screenRadius = sphere.r / fZ; Vector3Byte sphereColor = Vector3Byte.Lerp(sphere.colorA, sphere.colorB, colorLerpProgress); int centerY = (int)(screenY * _width / 2 + _width / 2); int centerX = (int)(screenX * _width / 2 + _width / 2); //For square 1024x1024 buffer - Rx == Ry int R = (int)(screenRadius * _width / 2); int Rsquared = R * R; int minX = Math.Max(centerX - R * 2, 0); int maxX = Math.Min(centerX + R * 2, _width - 1); int minY = Math.Max(centerY - R * 2, 0); int maxY = Math.Min(centerY + R * 2, _height - 1); for (int x = minX; x <= maxX; ++x) { for (int y = minY; y <= maxY; ++y) { int dx = x - centerX; int dy = y - centerY; int dx2 = dx * dx; int dy2 = dy * dy; //Skip pixels which are not part of a circle if (dx2 + dy2 > Rsquared) { continue; } if (screenZ < _zBuffer[x + y * _width]) { //lock (_zBuffer) { //Writing a float to _zBuffer array is atomic, no danger of corruption _zBuffer[x + y * _width] = screenZ; } // Phong shading { Vector3 vec_normal = new Vector3(); vec_normal.x = dx; vec_normal.y = dy; vec_normal.z = (float)Math.Sqrt(Rsquared - (dx2 + dy2)); vec_normal.Normalize(); float NdotL = lightDir.Dot(vec_normal); if (NdotL > 0) { Vector3 vec_eye = new Vector3(); vec_eye.x = lightDir.x + screenX; vec_eye.y = lightDir.y + screenY; vec_eye.z = lightDir.z + 1.0f; vec_eye.Normalize(); float NdotHV = vec_eye.Dot(vec_normal); float specular = NdotHV * NdotHV * NdotHV * NdotHV * NdotHV * NdotHV * NdotHV * NdotHV * NdotHV; // shininess=9 float alpha = NdotL + specular; alpha = Math.Min(alpha, 1.0f); byte r = (byte)(sphereColor.x * alpha); byte g = (byte)(sphereColor.y * alpha); byte b = (byte)(sphereColor.z * alpha); SetPixel(x, y, b, g, r); } else { //Unlit pixel SetPixel(x, y, 0, 0, 0); } } } } } }