Java制作软光栅化渲染器_艾孜尔江撰
2021/6/22 9:57:03
本文主要是介绍Java制作软光栅化渲染器_艾孜尔江撰,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
Main.java
:
import java.io.IOException; /** * The sole purpose of this class is to hold the main method. * * Any other use should be placed in a separate class */ public class Main { // Lazy exception handling here. You can do something more interesting // depending on what you're doing public static void main(String[] args) throws IOException { Display display = new Display(800, 600, "Software Rendering"); RenderContext target = display.GetFrameBuffer(); Bitmap texture = new Bitmap("./res/bricks.jpg"); Bitmap texture2 = new Bitmap("./res/bricks2.jpg"); Mesh monkeyMesh = new Mesh("./res/smoothMonkey0.obj"); Transform monkeyTransform = new Transform(new Vector4f(0,0.0f,3.0f)); Mesh terrainMesh = new Mesh("./res/terrain2.obj"); Transform terrainTransform = new Transform(new Vector4f(0,-1.0f,0.0f)); Camera camera = new Camera(new Matrix4f().InitPerspective((float)Math.toRadians(70.0f), (float)target.GetWidth()/(float)target.GetHeight(), 0.1f, 1000.0f)); float rotCounter = 0.0f; long previousTime = System.nanoTime(); while(true) { long currentTime = System.nanoTime(); float delta = (float)((currentTime - previousTime)/1000000000.0); previousTime = currentTime; camera.Update(display.GetInput(), delta); Matrix4f vp = camera.GetViewProjection(); monkeyTransform = monkeyTransform.Rotate(new Quaternion(new Vector4f(0,1,0), delta)); target.Clear((byte)0x00); target.ClearDepthBuffer(); monkeyMesh.Draw(target, vp, monkeyTransform.GetTransformation(), texture2); terrainMesh.Draw(target, vp, terrainTransform.GetTransformation(), texture); display.SwapBuffers(); } } }
Bitmap.java
:
public class Bitmap { /** The width, in pixels, of the image */ private final int m_width; /** The height, in pixels, of the image */ private final int m_height; /** Every pixel component in the image */ private final byte m_components[]; /** Basic getter */ public int GetWidth() { return m_width; } /** Basic getter */ public int GetHeight() { return m_height; } public byte GetComponent(int index) { return m_components[index]; } /** * Creates and initializes a Bitmap. * * @param width The width, in pixels, of the image. * @param height The height, in pixels, of the image. */ public Bitmap(int width, int height) { m_width = width; m_height = height; m_components = new byte[m_width * m_height * 4]; } public Bitmap(String fileName) throws IOException { int width = 0; int height = 0; byte[] components = null; BufferedImage image = ImageIO.read(new File(fileName)); width = image.getWidth(); height = image.getHeight(); int imgPixels[] = new int[width * height]; image.getRGB(0, 0, width, height, imgPixels, 0, width); components = new byte[width * height * 4]; for(int i = 0; i < width * height; i++) { int pixel = imgPixels[i]; components[i * 4] = (byte)((pixel >> 24) & 0xFF); // A components[i * 4 + 1] = (byte)((pixel ) & 0xFF); // B components[i * 4 + 2] = (byte)((pixel >> 8 ) & 0xFF); // G components[i * 4 + 3] = (byte)((pixel >> 16) & 0xFF); // R } m_width = width; m_height = height; m_components = components; } /** * Sets every pixel in the bitmap to a specific shade of grey. * * @param shade The shade of grey to use. 0 is black, 255 is white. */ public void Clear(byte shade) { Arrays.fill(m_components, shade); } /** * Sets the pixel at (x, y) to the color specified by (a,b,g,r). * * @param x Pixel location in X * @param y Pixel location in Y * @param a Alpha component * @param b Blue component * @param g Green component * @param r Red component */ public void DrawPixel(int x, int y, byte a, byte b, byte g, byte r) { int index = (x + y * m_width) * 4; m_components[index ] = a; m_components[index + 1] = b; m_components[index + 2] = g; m_components[index + 3] = r; } public void CopyPixel(int destX, int destY, int srcX, int srcY, Bitmap src, float lightAmt) { int destIndex = (destX + destY * m_width) * 4; int srcIndex = (srcX + srcY * src.GetWidth()) * 4; m_components[destIndex ] = (byte)((src.GetComponent(srcIndex) & 0xFF) * lightAmt); m_components[destIndex + 1] = (byte)((src.GetComponent(srcIndex + 1) & 0xFF) * lightAmt); m_components[destIndex + 2] = (byte)((src.GetComponent(srcIndex + 2) & 0xFF) * lightAmt); m_components[destIndex + 3] = (byte)((src.GetComponent(srcIndex + 3) & 0xFF) * lightAmt); } /** * Copies the Bitmap into a BGR byte array. * * @param dest The byte array to copy into. */ public void CopyToByteArray(byte[] dest) { for(int i = 0; i < m_width * m_height; i++) { dest[i * 3 ] = m_components[i * 4 + 1]; dest[i * 3 + 1] = m_components[i * 4 + 2]; dest[i * 3 + 2] = m_components[i * 4 + 3]; } } }
Camera.java
:
import java.awt.event.KeyEvent; public class Camera { private static final Vector4f Y_AXIS = new Vector4f(0,1,0); private Transform m_transform; private Matrix4f m_projection; private Transform GetTransform() { return m_transform; } public Camera(Matrix4f projection) { this.m_projection = projection; this.m_transform = new Transform(); } public Matrix4f GetViewProjection() { Matrix4f cameraRotation = GetTransform().GetTransformedRot().Conjugate().ToRotationMatrix(); Vector4f cameraPos = GetTransform().GetTransformedPos().Mul(-1); Matrix4f cameraTranslation = new Matrix4f().InitTranslation(cameraPos.GetX(), cameraPos.GetY(), cameraPos.GetZ()); return m_projection.Mul(cameraRotation.Mul(cameraTranslation)); } public void Update(Input input, float delta) { // Speed and rotation amounts are hardcoded here. // In a more general system, you might want to have them as variables. final float sensitivityX = 2.66f * delta; final float sensitivityY = 2.0f * delta; final float movAmt = 5.0f * delta; // Similarly, input keys are hardcoded here. // As before, in a more general system, you might want to have these as variables. if(input.GetKey(KeyEvent.VK_W)) Move(GetTransform().GetRot().GetForward(), movAmt); if(input.GetKey(KeyEvent.VK_S)) Move(GetTransform().GetRot().GetForward(), -movAmt); if(input.GetKey(KeyEvent.VK_A)) Move(GetTransform().GetRot().GetLeft(), movAmt); if(input.GetKey(KeyEvent.VK_D)) Move(GetTransform().GetRot().GetRight(), movAmt); if(input.GetKey(KeyEvent.VK_RIGHT)) Rotate(Y_AXIS, sensitivityX); if(input.GetKey(KeyEvent.VK_LEFT)) Rotate(Y_AXIS, -sensitivityX); if(input.GetKey(KeyEvent.VK_DOWN)) Rotate(GetTransform().GetRot().GetRight(), sensitivityY); if(input.GetKey(KeyEvent.VK_UP)) Rotate(GetTransform().GetRot().GetRight(), -sensitivityY); } private void Move(Vector4f dir, float amt) { m_transform = GetTransform().SetPos(GetTransform().GetPos().Add(dir.Mul(amt))); } private void Rotate(Vector4f axis, float angle) { m_transform = GetTransform().Rotate(new Quaternion(axis, angle)); } }
Display.java
:
import java.awt.Canvas; import java.awt.Graphics; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.awt.image.BufferStrategy; import java.awt.image.DataBufferByte; import javax.swing.JFrame; /** * Represents a window that can be drawn in using a software renderer. */ public class Display extends Canvas { /** The window being used for display */ private final JFrame m_frame; /** The bitmap representing the final image to display */ private final RenderContext m_frameBuffer; /** Used to display the framebuffer in the window */ private final BufferedImage m_displayImage; /** The pixels of the display image, as an array of byte components */ private final byte[] m_displayComponents; /** The buffers in the Canvas */ private final BufferStrategy m_bufferStrategy; /** A graphics object that can draw into the Canvas's buffers */ private final Graphics m_graphics; private final Input m_input; public RenderContext GetFrameBuffer() { return m_frameBuffer; } public Input GetInput() { return m_input; } /** * Creates and initializes a new display. * * @param width How wide the display is, in pixels. * @param height How tall the display is, in pixels. * @param title The text displayed in the window's title bar. */ public Display(int width, int height, String title) { //Set the canvas's preferred, minimum, and maximum size to prevent //unintentional resizing. Dimension size = new Dimension(width, height); setPreferredSize(size); setMinimumSize(size); setMaximumSize(size); //Creates images used for display. m_frameBuffer = new RenderContext(width, height); m_displayImage = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR); m_displayComponents = ((DataBufferByte)m_displayImage.getRaster().getDataBuffer()).getData(); m_frameBuffer.Clear((byte)0x80); m_frameBuffer.DrawPixel(100, 100, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0xFF); //Create a JFrame designed specifically to show this Display. m_frame = new JFrame(); m_frame.add(this); m_frame.pack(); m_frame.setResizable(false); m_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); m_frame.setLocationRelativeTo(null); m_frame.setTitle(title); m_frame.setSize(width, height); m_frame.setVisible(true); //Allocates 1 display buffer, and gets access to it via the buffer //strategy and a graphics object for drawing into it. createBufferStrategy(1); m_bufferStrategy = getBufferStrategy(); m_graphics = m_bufferStrategy.getDrawGraphics(); m_input = new Input(); addKeyListener(m_input); addFocusListener(m_input); addMouseListener(m_input); addMouseMotionListener(m_input); setFocusable(true); requestFocus(); } /** * Displays in the window. */ public void SwapBuffers() { //Display components should be the byte array used for displayImage's pixels. //Therefore, this call should effectively copy the frameBuffer into the //displayImage. m_frameBuffer.CopyToByteArray(m_displayComponents); m_graphics.drawImage(m_displayImage, 0, 0, m_frameBuffer.GetWidth(), m_frameBuffer.GetHeight(), null); m_bufferStrategy.show(); } }
Edge.java
:
public class Edge { private float m_x; private float m_xStep; private int m_yStart; private int m_yEnd; private float m_texCoordX; private float m_texCoordXStep; private float m_texCoordY; private float m_texCoordYStep; private float m_oneOverZ; private float m_oneOverZStep; private float m_depth; private float m_depthStep; private float m_lightAmt; private float m_lightAmtStep; public float GetX() { return m_x; } public int GetYStart() { return m_yStart; } public int GetYEnd() { return m_yEnd; } public float GetTexCoordX() { return m_texCoordX; } public float GetTexCoordY() { return m_texCoordY; } public float GetOneOverZ() { return m_oneOverZ; } public float GetDepth() { return m_depth; } public float GetLightAmt() { return m_lightAmt; } public Edge(Gradients gradients, Vertex minYVert, Vertex maxYVert, int minYVertIndex) { m_yStart = (int)Math.ceil(minYVert.GetY()); m_yEnd = (int)Math.ceil(maxYVert.GetY()); float yDist = maxYVert.GetY() - minYVert.GetY(); float xDist = maxYVert.GetX() - minYVert.GetX(); float yPrestep = m_yStart - minYVert.GetY(); m_xStep = (float)xDist/(float)yDist; m_x = minYVert.GetX() + yPrestep * m_xStep; float xPrestep = m_x - minYVert.GetX(); m_texCoordX = gradients.GetTexCoordX(minYVertIndex) + gradients.GetTexCoordXXStep() * xPrestep + gradients.GetTexCoordXYStep() * yPrestep; m_texCoordXStep = gradients.GetTexCoordXYStep() + gradients.GetTexCoordXXStep() * m_xStep; m_texCoordY = gradients.GetTexCoordY(minYVertIndex) + gradients.GetTexCoordYXStep() * xPrestep + gradients.GetTexCoordYYStep() * yPrestep; m_texCoordYStep = gradients.GetTexCoordYYStep() + gradients.GetTexCoordYXStep() * m_xStep; m_oneOverZ = gradients.GetOneOverZ(minYVertIndex) + gradients.GetOneOverZXStep() * xPrestep + gradients.GetOneOverZYStep() * yPrestep; m_oneOverZStep = gradients.GetOneOverZYStep() + gradients.GetOneOverZXStep() * m_xStep; m_depth = gradients.GetDepth(minYVertIndex) + gradients.GetDepthXStep() * xPrestep + gradients.GetDepthYStep() * yPrestep; m_depthStep = gradients.GetDepthYStep() + gradients.GetDepthXStep() * m_xStep; m_lightAmt = gradients.GetLightAmt(minYVertIndex) + gradients.GetLightAmtXStep() * xPrestep + gradients.GetLightAmtYStep() * yPrestep; m_lightAmtStep = gradients.GetLightAmtYStep() + gradients.GetLightAmtXStep() * m_xStep; } public void Step() { m_x += m_xStep; m_texCoordX += m_texCoordXStep; m_texCoordY += m_texCoordYStep; m_oneOverZ += m_oneOverZStep; m_depth += m_depthStep; m_lightAmt += m_lightAmtStep; } }
Gradients.java
:
public class Gradients { private float[] m_texCoordX; private float[] m_texCoordY; private float[] m_oneOverZ; private float[] m_depth; private float[] m_lightAmt; private float m_texCoordXXStep; private float m_texCoordXYStep; private float m_texCoordYXStep; private float m_texCoordYYStep; private float m_oneOverZXStep; private float m_oneOverZYStep; private float m_depthXStep; private float m_depthYStep; private float m_lightAmtXStep; private float m_lightAmtYStep; public float GetTexCoordX(int loc) { return m_texCoordX[loc]; } public float GetTexCoordY(int loc) { return m_texCoordY[loc]; } public float GetOneOverZ(int loc) { return m_oneOverZ[loc]; } public float GetDepth(int loc) { return m_depth[loc]; } public float GetLightAmt(int loc) { return m_lightAmt[loc]; } public float GetTexCoordXXStep() { return m_texCoordXXStep; } public float GetTexCoordXYStep() { return m_texCoordXYStep; } public float GetTexCoordYXStep() { return m_texCoordYXStep; } public float GetTexCoordYYStep() { return m_texCoordYYStep; } public float GetOneOverZXStep() { return m_oneOverZXStep; } public float GetOneOverZYStep() { return m_oneOverZYStep; } public float GetDepthXStep() { return m_depthXStep; } public float GetDepthYStep() { return m_depthYStep; } public float GetLightAmtXStep() { return m_lightAmtXStep; } public float GetLightAmtYStep() { return m_lightAmtYStep; } private float CalcXStep(float[] values, Vertex minYVert, Vertex midYVert, Vertex maxYVert, float oneOverdX) { return (((values[1] - values[2]) * (minYVert.GetY() - maxYVert.GetY())) - ((values[0] - values[2]) * (midYVert.GetY() - maxYVert.GetY()))) * oneOverdX; } private float CalcYStep(float[] values, Vertex minYVert, Vertex midYVert, Vertex maxYVert, float oneOverdY) { return (((values[1] - values[2]) * (minYVert.GetX() - maxYVert.GetX())) - ((values[0] - values[2]) * (midYVert.GetX() - maxYVert.GetX()))) * oneOverdY; } private float Saturate(float val) { if(val > 1.0f) { return 1.0f; } if(val < 0.0f) { return 0.0f; } return val; } public Gradients(Vertex minYVert, Vertex midYVert, Vertex maxYVert) { float oneOverdX = 1.0f / (((midYVert.GetX() - maxYVert.GetX()) * (minYVert.GetY() - maxYVert.GetY())) - ((minYVert.GetX() - maxYVert.GetX()) * (midYVert.GetY() - maxYVert.GetY()))); float oneOverdY = -oneOverdX; m_oneOverZ = new float[3]; m_texCoordX = new float[3]; m_texCoordY = new float[3]; m_depth = new float[3]; m_lightAmt = new float[3]; m_depth[0] = minYVert.GetPosition().GetZ(); m_depth[1] = midYVert.GetPosition().GetZ(); m_depth[2] = maxYVert.GetPosition().GetZ(); Vector4f lightDir = new Vector4f(0,0,1); m_lightAmt[0] = Saturate(minYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f; m_lightAmt[1] = Saturate(midYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f; m_lightAmt[2] = Saturate(maxYVert.GetNormal().Dot(lightDir)) * 0.9f + 0.1f; // Note that the W component is the perspective Z value; // The Z component is the occlusion Z value m_oneOverZ[0] = 1.0f/minYVert.GetPosition().GetW(); m_oneOverZ[1] = 1.0f/midYVert.GetPosition().GetW(); m_oneOverZ[2] = 1.0f/maxYVert.GetPosition().GetW(); m_texCoordX[0] = minYVert.GetTexCoords().GetX() * m_oneOverZ[0]; m_texCoordX[1] = midYVert.GetTexCoords().GetX() * m_oneOverZ[1]; m_texCoordX[2] = maxYVert.GetTexCoords().GetX() * m_oneOverZ[2]; m_texCoordY[0] = minYVert.GetTexCoords().GetY() * m_oneOverZ[0]; m_texCoordY[1] = midYVert.GetTexCoords().GetY() * m_oneOverZ[1]; m_texCoordY[2] = maxYVert.GetTexCoords().GetY() * m_oneOverZ[2]; m_texCoordXXStep = CalcXStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverdX); m_texCoordXYStep = CalcYStep(m_texCoordX, minYVert, midYVert, maxYVert, oneOverdY); m_texCoordYXStep = CalcXStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverdX); m_texCoordYYStep = CalcYStep(m_texCoordY, minYVert, midYVert, maxYVert, oneOverdY); m_oneOverZXStep = CalcXStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverdX); m_oneOverZYStep = CalcYStep(m_oneOverZ, minYVert, midYVert, maxYVert, oneOverdY); m_depthXStep = CalcXStep(m_depth, minYVert, midYVert, maxYVert, oneOverdX); m_depthYStep = CalcYStep(m_depth, minYVert, midYVert, maxYVert, oneOverdY); m_lightAmtXStep = CalcXStep(m_lightAmt, minYVert, midYVert, maxYVert, oneOverdX); m_lightAmtYStep = CalcYStep(m_lightAmt, minYVert, midYVert, maxYVert, oneOverdY); } }
IndexedModel.java
:
import java.util.ArrayList; import java.util.List; public class IndexedModel { private List<Vector4f> m_positions; private List<Vector4f> m_texCoords; private List<Vector4f> m_normals; private List<Vector4f> m_tangents; private List<Integer> m_indices; public IndexedModel() { m_positions = new ArrayList<Vector4f>(); m_texCoords = new ArrayList<Vector4f>(); m_normals = new ArrayList<Vector4f>(); m_tangents = new ArrayList<Vector4f>(); m_indices = new ArrayList<Integer>(); } public void CalcNormals() { for(int i = 0; i < m_indices.size(); i += 3) { int i0 = m_indices.get(i); int i1 = m_indices.get(i + 1); int i2 = m_indices.get(i + 2); Vector4f v1 = m_positions.get(i1).Sub(m_positions.get(i0)); Vector4f v2 = m_positions.get(i2).Sub(m_positions.get(i0)); Vector4f normal = v1.Cross(v2).Normalized(); m_normals.set(i0, m_normals.get(i0).Add(normal)); m_normals.set(i1, m_normals.get(i1).Add(normal)); m_normals.set(i2, m_normals.get(i2).Add(normal)); } for(int i = 0; i < m_normals.size(); i++) m_normals.set(i, m_normals.get(i).Normalized()); } public void CalcTangents() { for(int i = 0; i < m_indices.size(); i += 3) { int i0 = m_indices.get(i); int i1 = m_indices.get(i + 1); int i2 = m_indices.get(i + 2); Vector4f edge1 = m_positions.get(i1).Sub(m_positions.get(i0)); Vector4f edge2 = m_positions.get(i2).Sub(m_positions.get(i0)); float deltaU1 = m_texCoords.get(i1).GetX() - m_texCoords.get(i0).GetX(); float deltaV1 = m_texCoords.get(i1).GetY() - m_texCoords.get(i0).GetY(); float deltaU2 = m_texCoords.get(i2).GetX() - m_texCoords.get(i0).GetX(); float deltaV2 = m_texCoords.get(i2).GetY() - m_texCoords.get(i0).GetY(); float dividend = (deltaU1*deltaV2 - deltaU2*deltaV1); float f = dividend == 0 ? 0.0f : 1.0f/dividend; Vector4f tangent = new Vector4f( f * (deltaV2 * edge1.GetX() - deltaV1 * edge2.GetX()), f * (deltaV2 * edge1.GetY() - deltaV1 * edge2.GetY()), f * (deltaV2 * edge1.GetZ() - deltaV1 * edge2.GetZ()), 0); m_tangents.set(i0, m_tangents.get(i0).Add(tangent)); m_tangents.set(i1, m_tangents.get(i1).Add(tangent)); m_tangents.set(i2, m_tangents.get(i2).Add(tangent)); } for(int i = 0; i < m_tangents.size(); i++) m_tangents.set(i, m_tangents.get(i).Normalized()); } public List<Vector4f> GetPositions() { return m_positions; } public List<Vector4f> GetTexCoords() { return m_texCoords; } public List<Vector4f> GetNormals() { return m_normals; } public List<Vector4f> GetTangents() { return m_tangents; } public List<Integer> GetIndices() { return m_indices; } }
Input.java
:
import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; /** * Stores the current state of any user input devices, and updates them with new * input events. * * @author Benny Bobaganoosh (thebennybox@gmail.com) */ public class Input implements KeyListener, FocusListener, MouseListener, MouseMotionListener { private boolean[] keys = new boolean[65536]; private boolean[] mouseButtons = new boolean[4]; private int mouseX = 0; private int mouseY = 0; /** Updates state when the mouse is dragged */ public void mouseDragged(MouseEvent e) { mouseX = e.getX(); mouseY = e.getY(); } /** Updates state when the mouse is moved */ public void mouseMoved(MouseEvent e) { mouseX = e.getX(); mouseY = e.getY(); } /** Updates state when the mouse is clicked */ public void mouseClicked(MouseEvent e) { } /** Updates state when the mouse enters the screen */ public void mouseEntered(MouseEvent e) { } /** Updates state when the mouse exits the screen */ public void mouseExited(MouseEvent e) { } /** Updates state when a mouse button is pressed */ public void mousePressed(MouseEvent e) { int code = e.getButton(); if (code > 0 && code < mouseButtons.length) mouseButtons[code] = true; } /** Updates state when a mouse button is released */ public void mouseReleased(MouseEvent e) { int code = e.getButton(); if (code > 0 && code < mouseButtons.length) mouseButtons[code] = false; } /** Updates state when the window gains focus */ public void focusGained(FocusEvent e) { } /** Updates state when the window loses focus */ public void focusLost(FocusEvent e) { for (int i = 0; i < keys.length; i++) keys[i] = false; for (int i = 0; i < mouseButtons.length; i++) mouseButtons[i] = false; } /** Updates state when a key is pressed */ public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); if (code > 0 && code < keys.length) keys[code] = true; } /** Updates state when a key is released */ public void keyReleased(KeyEvent e) { int code = e.getKeyCode(); if (code > 0 && code < keys.length) keys[code] = false; } /** Updates state when a key is typed */ public void keyTyped(KeyEvent e) { } /** * Gets whether or not a particular key is currently pressed. * * @param key The key to test * @return Whether or not key is currently pressed. */ public boolean GetKey(int key) { return keys[key]; } /** * Gets whether or not a particular mouse button is currently pressed. * * @param button The button to test * @return Whether or not the button is currently pressed. */ public boolean GetMouse(int button) { return mouseButtons[button]; } /** * Gets the location of the mouse cursor on x, in pixels. * @return The location of the mouse cursor on x, in pixels */ public int GetMouseX() { return mouseX; } /** * Gets the location of the mouse cursor on y, in pixels. * @return The location of the mouse cursor on y, in pixels */ public int GetMouseY() { return mouseY; } }
Matrix4f.java
:
public class Matrix4f { private float[][] m; public Matrix4f() { m = new float[4][4]; } public Matrix4f InitIdentity() { m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = 0; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitScreenSpaceTransform(float halfWidth, float halfHeight) { m[0][0] = halfWidth; m[0][1] = 0; m[0][2] = 0; m[0][3] = halfWidth - 0.5f; m[1][0] = 0; m[1][1] = -halfHeight; m[1][2] = 0; m[1][3] = halfHeight - 0.5f; m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = 0; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitTranslation(float x, float y, float z) { m[0][0] = 1; m[0][1] = 0; m[0][2] = 0; m[0][3] = x; m[1][0] = 0; m[1][1] = 1; m[1][2] = 0; m[1][3] = y; m[2][0] = 0; m[2][1] = 0; m[2][2] = 1; m[2][3] = z; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitRotation(float x, float y, float z, float angle) { float sin = (float)Math.sin(angle); float cos = (float)Math.cos(angle); m[0][0] = cos+x*x*(1-cos); m[0][1] = x*y*(1-cos)-z*sin; m[0][2] = x*z*(1-cos)+y*sin; m[0][3] = 0; m[1][0] = y*x*(1-cos)+z*sin; m[1][1] = cos+y*y*(1-cos); m[1][2] = y*z*(1-cos)-x*sin; m[1][3] = 0; m[2][0] = z*x*(1-cos)-y*sin; m[2][1] = z*y*(1-cos)+x*sin; m[2][2] = cos+z*z*(1-cos); m[2][3] = 0; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitRotation(float x, float y, float z) { Matrix4f rx = new Matrix4f(); Matrix4f ry = new Matrix4f(); Matrix4f rz = new Matrix4f(); rz.m[0][0] = (float)Math.cos(z);rz.m[0][1] = -(float)Math.sin(z);rz.m[0][2] = 0; rz.m[0][3] = 0; rz.m[1][0] = (float)Math.sin(z);rz.m[1][1] = (float)Math.cos(z);rz.m[1][2] = 0; rz.m[1][3] = 0; rz.m[2][0] = 0; rz.m[2][1] = 0; rz.m[2][2] = 1; rz.m[2][3] = 0; rz.m[3][0] = 0; rz.m[3][1] = 0; rz.m[3][2] = 0; rz.m[3][3] = 1; rx.m[0][0] = 1; rx.m[0][1] = 0; rx.m[0][2] = 0; rx.m[0][3] = 0; rx.m[1][0] = 0; rx.m[1][1] = (float)Math.cos(x);rx.m[1][2] = -(float)Math.sin(x);rx.m[1][3] = 0; rx.m[2][0] = 0; rx.m[2][1] = (float)Math.sin(x);rx.m[2][2] = (float)Math.cos(x);rx.m[2][3] = 0; rx.m[3][0] = 0; rx.m[3][1] = 0; rx.m[3][2] = 0; rx.m[3][3] = 1; ry.m[0][0] = (float)Math.cos(y);ry.m[0][1] = 0; ry.m[0][2] = -(float)Math.sin(y);ry.m[0][3] = 0; ry.m[1][0] = 0; ry.m[1][1] = 1; ry.m[1][2] = 0; ry.m[1][3] = 0; ry.m[2][0] = (float)Math.sin(y);ry.m[2][1] = 0; ry.m[2][2] = (float)Math.cos(y);ry.m[2][3] = 0; ry.m[3][0] = 0; ry.m[3][1] = 0; ry.m[3][2] = 0; ry.m[3][3] = 1; m = rz.Mul(ry.Mul(rx)).GetM(); return this; } public Matrix4f InitScale(float x, float y, float z) { m[0][0] = x; m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; m[1][0] = 0; m[1][1] = y; m[1][2] = 0; m[1][3] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = z; m[2][3] = 0; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitPerspective(float fov, float aspectRatio, float zNear, float zFar) { float tanHalfFOV = (float)Math.tan(fov / 2); float zRange = zNear - zFar; m[0][0] = 1.0f / (tanHalfFOV * aspectRatio); m[0][1] = 0; m[0][2] = 0; m[0][3] = 0; m[1][0] = 0; m[1][1] = 1.0f / tanHalfFOV; m[1][2] = 0; m[1][3] = 0; m[2][0] = 0; m[2][1] = 0; m[2][2] = (-zNear -zFar)/zRange; m[2][3] = 2 * zFar * zNear / zRange; m[3][0] = 0; m[3][1] = 0; m[3][2] = 1; m[3][3] = 0; return this; } public Matrix4f InitOrthographic(float left, float right, float bottom, float top, float near, float far) { float width = right - left; float height = top - bottom; float depth = far - near; m[0][0] = 2/width;m[0][1] = 0; m[0][2] = 0; m[0][3] = -(right + left)/width; m[1][0] = 0; m[1][1] = 2/height;m[1][2] = 0; m[1][3] = -(top + bottom)/height; m[2][0] = 0; m[2][1] = 0; m[2][2] = -2/depth;m[2][3] = -(far + near)/depth; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Matrix4f InitRotation(Vector4f forward, Vector4f up) { Vector4f f = forward.Normalized(); Vector4f r = up.Normalized(); r = r.Cross(f); Vector4f u = f.Cross(r); return InitRotation(f, u, r); } public Matrix4f InitRotation(Vector4f forward, Vector4f up, Vector4f right) { Vector4f f = forward; Vector4f r = right; Vector4f u = up; m[0][0] = r.GetX(); m[0][1] = r.GetY(); m[0][2] = r.GetZ(); m[0][3] = 0; m[1][0] = u.GetX(); m[1][1] = u.GetY(); m[1][2] = u.GetZ(); m[1][3] = 0; m[2][0] = f.GetX(); m[2][1] = f.GetY(); m[2][2] = f.GetZ(); m[2][3] = 0; m[3][0] = 0; m[3][1] = 0; m[3][2] = 0; m[3][3] = 1; return this; } public Vector4f Transform(Vector4f r) { return new Vector4f(m[0][0] * r.GetX() + m[0][1] * r.GetY() + m[0][2] * r.GetZ() + m[0][3] * r.GetW(), m[1][0] * r.GetX() + m[1][1] * r.GetY() + m[1][2] * r.GetZ() + m[1][3] * r.GetW(), m[2][0] * r.GetX() + m[2][1] * r.GetY() + m[2][2] * r.GetZ() + m[2][3] * r.GetW(), m[3][0] * r.GetX() + m[3][1] * r.GetY() + m[3][2] * r.GetZ() + m[3][3] * r.GetW()); } public Matrix4f Mul(Matrix4f r) { Matrix4f res = new Matrix4f(); for(int i = 0; i < 4; i++) { for(int j = 0; j < 4; j++) { res.Set(i, j, m[i][0] * r.Get(0, j) + m[i][1] * r.Get(1, j) + m[i][2] * r.Get(2, j) + m[i][3] * r.Get(3, j)); } } return res; } public float[][] GetM() { float[][] res = new float[4][4]; for(int i = 0; i < 4; i++) for(int j = 0; j < 4; j++) res[i][j] = m[i][j]; return res; } public float Get(int x, int y) { return m[x][y]; } public void SetM(float[][] m) { this.m = m; } public void Set(int x, int y, float value) { m[x][y] = value; } }
Mesh.java
:
import java.util.List; import java.util.ArrayList; import java.io.IOException; public class Mesh { private List<Vertex> m_vertices; private List<Integer> m_indices; public Mesh(String fileName) throws IOException { IndexedModel model = new OBJModel(fileName).ToIndexedModel(); m_vertices = new ArrayList<Vertex>(); for(int i = 0; i < model.GetPositions().size(); i++) { m_vertices.add(new Vertex( model.GetPositions().get(i), model.GetTexCoords().get(i), model.GetNormals().get(i))); } m_indices = model.GetIndices(); } public void Draw(RenderContext context, Matrix4f viewProjection, Matrix4f transform, Bitmap texture) { Matrix4f mvp = viewProjection.Mul(transform); for(int i = 0; i < m_indices.size(); i += 3) { context.DrawTriangle( m_vertices.get(m_indices.get(i)).Transform(mvp, transform), m_vertices.get(m_indices.get(i + 1)).Transform(mvp, transform), m_vertices.get(m_indices.get(i + 2)).Transform(mvp, transform), texture); } } }
OBJModel.java
:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.HashMap; import java.util.Map; public class OBJModel { private class OBJIndex { private int m_vertexIndex; private int m_texCoordIndex; private int m_normalIndex; public int GetVertexIndex() { return m_vertexIndex; } public int GetTexCoordIndex() { return m_texCoordIndex; } public int GetNormalIndex() { return m_normalIndex; } public void SetVertexIndex(int val) { m_vertexIndex = val; } public void SetTexCoordIndex(int val) { m_texCoordIndex = val; } public void SetNormalIndex(int val) { m_normalIndex = val; } @Override public boolean equals(Object obj) { OBJIndex index = (OBJIndex)obj; return m_vertexIndex == index.m_vertexIndex && m_texCoordIndex == index.m_texCoordIndex && m_normalIndex == index.m_normalIndex; } @Override public int hashCode() { final int BASE = 17; final int MULTIPLIER = 31; int result = BASE; result = MULTIPLIER * result + m_vertexIndex; result = MULTIPLIER * result + m_texCoordIndex; result = MULTIPLIER * result + m_normalIndex; return result; } } private List<Vector4f> m_positions; private List<Vector4f> m_texCoords; private List<Vector4f> m_normals; private List<OBJIndex> m_indices; private boolean m_hasTexCoords; private boolean m_hasNormals; private static String[] RemoveEmptyStrings(String[] data) { List<String> result = new ArrayList<String>(); for(int i = 0; i < data.length; i++) if(!data[i].equals("")) result.add(data[i]); String[] res = new String[result.size()]; result.toArray(res); return res; } public OBJModel(String fileName) throws IOException { m_positions = new ArrayList<Vector4f>(); m_texCoords = new ArrayList<Vector4f>(); m_normals = new ArrayList<Vector4f>(); m_indices = new ArrayList<OBJIndex>(); m_hasTexCoords = false; m_hasNormals = false; BufferedReader meshReader = null; meshReader = new BufferedReader(new FileReader(fileName)); String line; while((line = meshReader.readLine()) != null) { String[] tokens = line.split(" "); tokens = RemoveEmptyStrings(tokens); if(tokens.length == 0 || tokens[0].equals("#")) continue; else if(tokens[0].equals("v")) { m_positions.add(new Vector4f(Float.valueOf(tokens[1]), Float.valueOf(tokens[2]), Float.valueOf(tokens[3]),1)); } else if(tokens[0].equals("vt")) { m_texCoords.add(new Vector4f(Float.valueOf(tokens[1]), 1.0f - Float.valueOf(tokens[2]),0,0)); } else if(tokens[0].equals("vn")) { m_normals.add(new Vector4f(Float.valueOf(tokens[1]), Float.valueOf(tokens[2]), Float.valueOf(tokens[3]),0)); } else if(tokens[0].equals("f")) { for(int i = 0; i < tokens.length - 3; i++) { m_indices.add(ParseOBJIndex(tokens[1])); m_indices.add(ParseOBJIndex(tokens[2 + i])); m_indices.add(ParseOBJIndex(tokens[3 + i])); } } } meshReader.close(); } public IndexedModel ToIndexedModel() { IndexedModel result = new IndexedModel(); IndexedModel normalModel = new IndexedModel(); Map<OBJIndex, Integer> resultIndexMap = new HashMap<OBJIndex, Integer>(); Map<Integer, Integer> normalIndexMap = new HashMap<Integer, Integer>(); Map<Integer, Integer> indexMap = new HashMap<Integer, Integer>(); for(int i = 0; i < m_indices.size(); i++) { OBJIndex currentIndex = m_indices.get(i); Vector4f currentPosition = m_positions.get(currentIndex.GetVertexIndex()); Vector4f currentTexCoord; Vector4f currentNormal; if(m_hasTexCoords) currentTexCoord = m_texCoords.get(currentIndex.GetTexCoordIndex()); else currentTexCoord = new Vector4f(0,0,0,0); if(m_hasNormals) currentNormal = m_normals.get(currentIndex.GetNormalIndex()); else currentNormal = new Vector4f(0,0,0,0); Integer modelVertexIndex = resultIndexMap.get(currentIndex); if(modelVertexIndex == null) { modelVertexIndex = result.GetPositions().size(); resultIndexMap.put(currentIndex, modelVertexIndex); result.GetPositions().add(currentPosition); result.GetTexCoords().add(currentTexCoord); if(m_hasNormals) result.GetNormals().add(currentNormal); } Integer normalModelIndex = normalIndexMap.get(currentIndex.GetVertexIndex()); if(normalModelIndex == null) { normalModelIndex = normalModel.GetPositions().size(); normalIndexMap.put(currentIndex.GetVertexIndex(), normalModelIndex); normalModel.GetPositions().add(currentPosition); normalModel.GetTexCoords().add(currentTexCoord); normalModel.GetNormals().add(currentNormal); normalModel.GetTangents().add(new Vector4f(0,0,0,0)); } result.GetIndices().add(modelVertexIndex); normalModel.GetIndices().add(normalModelIndex); indexMap.put(modelVertexIndex, normalModelIndex); } if(!m_hasNormals) { normalModel.CalcNormals(); for(int i = 0; i < result.GetPositions().size(); i++) result.GetNormals().add(normalModel.GetNormals().get(indexMap.get(i))); } normalModel.CalcTangents(); for(int i = 0; i < result.GetPositions().size(); i++) result.GetTangents().add(normalModel.GetTangents().get(indexMap.get(i))); return result; } private OBJIndex ParseOBJIndex(String token) { String[] values = token.split("/"); OBJIndex result = new OBJIndex(); result.SetVertexIndex(Integer.parseInt(values[0]) - 1); if(values.length > 1) { if(!values[1].isEmpty()) { m_hasTexCoords = true; result.SetTexCoordIndex(Integer.parseInt(values[1]) - 1); } if(values.length > 2) { m_hasNormals = true; result.SetNormalIndex(Integer.parseInt(values[2]) - 1); } } return result; } }
Quaternion.java
:
public class Quaternion { private float m_x; private float m_y; private float m_z; private float m_w; public Quaternion(float x, float y, float z, float w) { this.m_x = x; this.m_y = y; this.m_z = z; this.m_w = w; } public Quaternion(Vector4f axis, float angle) { float sinHalfAngle = (float)Math.sin(angle / 2); float cosHalfAngle = (float)Math.cos(angle / 2); this.m_x = axis.GetX() * sinHalfAngle; this.m_y = axis.GetY() * sinHalfAngle; this.m_z = axis.GetZ() * sinHalfAngle; this.m_w = cosHalfAngle; } public float Length() { return (float)Math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w); } public Quaternion Normalized() { float length = Length(); return new Quaternion(m_x / length, m_y / length, m_z / length, m_w / length); } public Quaternion Conjugate() { return new Quaternion(-m_x, -m_y, -m_z, m_w); } public Quaternion Mul(float r) { return new Quaternion(m_x * r, m_y * r, m_z * r, m_w * r); } public Quaternion Mul(Quaternion r) { float w_ = m_w * r.GetW() - m_x * r.GetX() - m_y * r.GetY() - m_z * r.GetZ(); float x_ = m_x * r.GetW() + m_w * r.GetX() + m_y * r.GetZ() - m_z * r.GetY(); float y_ = m_y * r.GetW() + m_w * r.GetY() + m_z * r.GetX() - m_x * r.GetZ(); float z_ = m_z * r.GetW() + m_w * r.GetZ() + m_x * r.GetY() - m_y * r.GetX(); return new Quaternion(x_, y_, z_, w_); } public Quaternion Mul(Vector4f r) { float w_ = -m_x * r.GetX() - m_y * r.GetY() - m_z * r.GetZ(); float x_ = m_w * r.GetX() + m_y * r.GetZ() - m_z * r.GetY(); float y_ = m_w * r.GetY() + m_z * r.GetX() - m_x * r.GetZ(); float z_ = m_w * r.GetZ() + m_x * r.GetY() - m_y * r.GetX(); return new Quaternion(x_, y_, z_, w_); } public Quaternion Sub(Quaternion r) { return new Quaternion(m_x - r.GetX(), m_y - r.GetY(), m_z - r.GetZ(), m_w - r.GetW()); } public Quaternion Add(Quaternion r) { return new Quaternion(m_x + r.GetX(), m_y + r.GetY(), m_z + r.GetZ(), m_w + r.GetW()); } public Matrix4f ToRotationMatrix() { Vector4f forward = new Vector4f(2.0f * (m_x * m_z - m_w * m_y), 2.0f * (m_y * m_z + m_w * m_x), 1.0f - 2.0f * (m_x * m_x + m_y * m_y)); Vector4f up = new Vector4f(2.0f * (m_x * m_y + m_w * m_z), 1.0f - 2.0f * (m_x * m_x + m_z * m_z), 2.0f * (m_y * m_z - m_w * m_x)); Vector4f right = new Vector4f(1.0f - 2.0f * (m_y * m_y + m_z * m_z), 2.0f * (m_x * m_y - m_w * m_z), 2.0f * (m_x * m_z + m_w * m_y)); return new Matrix4f().InitRotation(forward, up, right); } public float Dot(Quaternion r) { return m_x * r.GetX() + m_y * r.GetY() + m_z * r.GetZ() + m_w * r.GetW(); } public Quaternion NLerp(Quaternion dest, float lerpFactor, boolean shortest) { Quaternion correctedDest = dest; if(shortest && this.Dot(dest) < 0) correctedDest = new Quaternion(-dest.GetX(), -dest.GetY(), -dest.GetZ(), -dest.GetW()); return correctedDest.Sub(this).Mul(lerpFactor).Add(this).Normalized(); } public Quaternion SLerp(Quaternion dest, float lerpFactor, boolean shortest) { final float EPSILON = 1e3f; float cos = this.Dot(dest); Quaternion correctedDest = dest; if(shortest && cos < 0) { cos = -cos; correctedDest = new Quaternion(-dest.GetX(), -dest.GetY(), -dest.GetZ(), -dest.GetW()); } if(Math.abs(cos) >= 1 - EPSILON) return NLerp(correctedDest, lerpFactor, false); float sin = (float)Math.sqrt(1.0f - cos * cos); float angle = (float)Math.atan2(sin, cos); float invSin = 1.0f/sin; float srcFactor = (float)Math.sin((1.0f - lerpFactor) * angle) * invSin; float destFactor = (float)Math.sin((lerpFactor) * angle) * invSin; return this.Mul(srcFactor).Add(correctedDest.Mul(destFactor)); } //From Ken Shoemake's "Quaternion Calculus and Fast Animation" article public Quaternion(Matrix4f rot) { float trace = rot.Get(0, 0) + rot.Get(1, 1) + rot.Get(2, 2); if(trace > 0) { float s = 0.5f / (float)Math.sqrt(trace+ 1.0f); m_w = 0.25f / s; m_x = (rot.Get(1, 2) - rot.Get(2, 1)) * s; m_y = (rot.Get(2, 0) - rot.Get(0, 2)) * s; m_z = (rot.Get(0, 1) - rot.Get(1, 0)) * s; } else { if(rot.Get(0, 0) > rot.Get(1, 1) && rot.Get(0, 0) > rot.Get(2, 2)) { float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(0, 0) - rot.Get(1, 1) - rot.Get(2, 2)); m_w = (rot.Get(1, 2) - rot.Get(2, 1)) / s; m_x = 0.25f * s; m_y = (rot.Get(1, 0) + rot.Get(0, 1)) / s; m_z = (rot.Get(2, 0) + rot.Get(0, 2)) / s; } else if(rot.Get(1, 1) > rot.Get(2, 2)) { float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(1, 1) - rot.Get(0, 0) - rot.Get(2, 2)); m_w = (rot.Get(2, 0) - rot.Get(0, 2)) / s; m_x = (rot.Get(1, 0) + rot.Get(0, 1)) / s; m_y = 0.25f * s; m_z = (rot.Get(2, 1) + rot.Get(1, 2)) / s; } else { float s = 2.0f * (float)Math.sqrt(1.0f + rot.Get(2, 2) - rot.Get(0, 0) - rot.Get(1, 1)); m_w = (rot.Get(0, 1) - rot.Get(1, 0) ) / s; m_x = (rot.Get(2, 0) + rot.Get(0, 2) ) / s; m_y = (rot.Get(1, 2) + rot.Get(2, 1) ) / s; m_z = 0.25f * s; } } float length = (float)Math.sqrt(m_x * m_x + m_y * m_y + m_z * m_z + m_w * m_w); m_x /= length; m_y /= length; m_z /= length; m_w /= length; } public Vector4f GetForward() { return new Vector4f(0,0,1,1).Rotate(this); } public Vector4f GetBack() { return new Vector4f(0,0,-1,1).Rotate(this); } public Vector4f GetUp() { return new Vector4f(0,1,0,1).Rotate(this); } public Vector4f GetDown() { return new Vector4f(0,-1,0,1).Rotate(this); } public Vector4f GetRight() { return new Vector4f(1,0,0,1).Rotate(this); } public Vector4f GetLeft() { return new Vector4f(-1,0,0,1).Rotate(this); } public float GetX() { return m_x; } public float GetY() { return m_y; } public float GetZ() { return m_z; } public float GetW() { return m_w; } public boolean equals(Quaternion r) { return m_x == r.GetX() && m_y == r.GetY() && m_z == r.GetZ() && m_w == r.GetW(); } }
RenderContext.java
:
import java.util.List; import java.util.ArrayList; import java.util.Iterator; public class RenderContext extends Bitmap { private float[] m_zBuffer; public RenderContext(int width, int height) { super(width, height); m_zBuffer = new float[width * height]; } public void ClearDepthBuffer() { for(int i = 0; i < m_zBuffer.length; i++) { m_zBuffer[i] = Float.MAX_VALUE; } } public void DrawTriangle(Vertex v1, Vertex v2, Vertex v3, Bitmap texture) { if(v1.IsInsideViewFrustum() && v2.IsInsideViewFrustum() && v3.IsInsideViewFrustum()) { FillTriangle(v1, v2, v3, texture); return; } List<Vertex> vertices = new ArrayList<>(); List<Vertex> auxillaryList = new ArrayList<>(); vertices.add(v1); vertices.add(v2); vertices.add(v3); if(ClipPolygonAxis(vertices, auxillaryList, 0) && ClipPolygonAxis(vertices, auxillaryList, 1) && ClipPolygonAxis(vertices, auxillaryList, 2)) { Vertex initialVertex = vertices.get(0); for(int i = 1; i < vertices.size() - 1; i++) { FillTriangle(initialVertex, vertices.get(i), vertices.get(i + 1), texture); } } } private boolean ClipPolygonAxis(List<Vertex> vertices, List<Vertex> auxillaryList, int componentIndex) { ClipPolygonComponent(vertices, componentIndex, 1.0f, auxillaryList); vertices.clear(); if(auxillaryList.isEmpty()) { return false; } ClipPolygonComponent(auxillaryList, componentIndex, -1.0f, vertices); auxillaryList.clear(); return !vertices.isEmpty(); } private void ClipPolygonComponent(List<Vertex> vertices, int componentIndex, float componentFactor, List<Vertex> result) { Vertex previousVertex = vertices.get(vertices.size() - 1); float previousComponent = previousVertex.Get(componentIndex) * componentFactor; boolean previousInside = previousComponent <= previousVertex.GetPosition().GetW(); Iterator<Vertex> it = vertices.iterator(); while(it.hasNext()) { Vertex currentVertex = it.next(); float currentComponent = currentVertex.Get(componentIndex) * componentFactor; boolean currentInside = currentComponent <= currentVertex.GetPosition().GetW(); if(currentInside ^ previousInside) { float lerpAmt = (previousVertex.GetPosition().GetW() - previousComponent) / ((previousVertex.GetPosition().GetW() - previousComponent) - (currentVertex.GetPosition().GetW() - currentComponent)); result.add(previousVertex.Lerp(currentVertex, lerpAmt)); } if(currentInside) { result.add(currentVertex); } previousVertex = currentVertex; previousComponent = currentComponent; previousInside = currentInside; } } private void FillTriangle(Vertex v1, Vertex v2, Vertex v3, Bitmap texture) { Matrix4f screenSpaceTransform = new Matrix4f().InitScreenSpaceTransform(GetWidth()/2, GetHeight()/2); Matrix4f identity = new Matrix4f().InitIdentity(); Vertex minYVert = v1.Transform(screenSpaceTransform, identity).PerspectiveDivide(); Vertex midYVert = v2.Transform(screenSpaceTransform, identity).PerspectiveDivide(); Vertex maxYVert = v3.Transform(screenSpaceTransform, identity).PerspectiveDivide(); if(minYVert.TriangleAreaTimesTwo(maxYVert, midYVert) >= 0) { return; } if(maxYVert.GetY() < midYVert.GetY()) { Vertex temp = maxYVert; maxYVert = midYVert; midYVert = temp; } if(midYVert.GetY() < minYVert.GetY()) { Vertex temp = midYVert; midYVert = minYVert; minYVert = temp; } if(maxYVert.GetY() < midYVert.GetY()) { Vertex temp = maxYVert; maxYVert = midYVert; midYVert = temp; } ScanTriangle(minYVert, midYVert, maxYVert, minYVert.TriangleAreaTimesTwo(maxYVert, midYVert) >= 0, texture); } private void ScanTriangle(Vertex minYVert, Vertex midYVert, Vertex maxYVert, boolean handedness, Bitmap texture) { Gradients gradients = new Gradients(minYVert, midYVert, maxYVert); Edge topToBottom = new Edge(gradients, minYVert, maxYVert, 0); Edge topToMiddle = new Edge(gradients, minYVert, midYVert, 0); Edge middleToBottom = new Edge(gradients, midYVert, maxYVert, 1); ScanEdges(gradients, topToBottom, topToMiddle, handedness, texture); ScanEdges(gradients, topToBottom, middleToBottom, handedness, texture); } private void ScanEdges(Gradients gradients, Edge a, Edge b, boolean handedness, Bitmap texture) { Edge left = a; Edge right = b; if(handedness) { Edge temp = left; left = right; right = temp; } int yStart = b.GetYStart(); int yEnd = b.GetYEnd(); for(int j = yStart; j < yEnd; j++) { DrawScanLine(gradients, left, right, j, texture); left.Step(); right.Step(); } } private void DrawScanLine(Gradients gradients, Edge left, Edge right, int j, Bitmap texture) { int xMin = (int)Math.ceil(left.GetX()); int xMax = (int)Math.ceil(right.GetX()); float xPrestep = xMin - left.GetX(); // float xDist = right.GetX() - left.GetX(); // float texCoordXXStep = (right.GetTexCoordX() - left.GetTexCoordX())/xDist; // float texCoordYXStep = (right.GetTexCoordY() - left.GetTexCoordY())/xDist; // float oneOverZXStep = (right.GetOneOverZ() - left.GetOneOverZ())/xDist; // float depthXStep = (right.GetDepth() - left.GetDepth())/xDist; // Apparently, now that stepping is actually on pixel centers, gradients are // precise enough again. float texCoordXXStep = gradients.GetTexCoordXXStep(); float texCoordYXStep = gradients.GetTexCoordYXStep(); float oneOverZXStep = gradients.GetOneOverZXStep(); float depthXStep = gradients.GetDepthXStep(); float lightAmtXStep = gradients.GetLightAmtXStep(); float texCoordX = left.GetTexCoordX() + texCoordXXStep * xPrestep; float texCoordY = left.GetTexCoordY() + texCoordYXStep * xPrestep; float oneOverZ = left.GetOneOverZ() + oneOverZXStep * xPrestep; float depth = left.GetDepth() + depthXStep * xPrestep; float lightAmt = left.GetLightAmt() + lightAmtXStep * xPrestep; for(int i = xMin; i < xMax; i++) { int index = i + j * GetWidth(); if(depth < m_zBuffer[index]) { m_zBuffer[index] = depth; float z = 1.0f/oneOverZ; int srcX = (int)((texCoordX * z) * (float)(texture.GetWidth() - 1) + 0.5f); int srcY = (int)((texCoordY * z) * (float)(texture.GetHeight() - 1) + 0.5f); CopyPixel(i, j, srcX, srcY, texture, lightAmt); } oneOverZ += oneOverZXStep; texCoordX += texCoordXXStep; texCoordY += texCoordYXStep; depth += depthXStep; lightAmt += lightAmtXStep; } } }
Stars3D.java
:
import java.io.IOException; /** * Represents a 3D Star field that can be rendered into an image. */ public class Stars3D { /** How much the stars are spread out in 3D space, on average. */ private final float m_spread; /** How quickly the stars move towards the camera */ private final float m_speed; /** The star positions on the X axis */ private final float m_starX[]; /** The star positions on the Y axis */ private final float m_starY[]; /** The star positions on the Z axis */ private final float m_starZ[]; /** * Creates a new 3D star field in a usable state. * * @param numStars The number of stars in the star field * @param spread How much the stars spread out, on average. * @param speed How quickly the stars move towards the camera */ public Stars3D(int numStars, float spread, float speed) throws IOException { m_spread = spread; m_speed = speed; m_starX = new float[numStars]; m_starY = new float[numStars]; m_starZ = new float[numStars]; for(int i = 0; i < m_starX.length; i++) { InitStar(i); } m_bitmap = new Bitmap("./res/bricks.jpg"); } private final Bitmap m_bitmap; /** * Initializes a star to a new pseudo-random location in 3D space. * * @param i The index of the star to initialize. */ private void InitStar(int i) { //The random values have 0.5 subtracted from them and are multiplied //by 2 to remap them from the range (0, 1) to (-1, 1). m_starX[i] = 2 * ((float)Math.random() - 0.5f) * m_spread; m_starY[i] = 2 * ((float)Math.random() - 0.5f) * m_spread; //For Z, the random value is only adjusted by a small amount to stop //a star from being generated at 0 on Z. m_starZ[i] = ((float)Math.random() + 0.00001f) * m_spread; } /** * Updates every star to a new position, and draws the starfield in a * bitmap. * * @param target The bitmap to render to. * @param delta How much time has passed since the last update. */ public void UpdateAndRender(RenderContext target, float delta) { final float tanHalfFOV = (float)Math.tan(Math.toRadians(90.0/2.0)); //Stars are drawn on a black background target.Clear((byte)0x00); float halfWidth = target.GetWidth()/2.0f; float halfHeight = target.GetHeight()/2.0f; int triangleBuilderCounter = 0; int x1 = 0; int y1 = 0; int x2 = 0; int y2 = 0; for(int i = 0; i < m_starX.length; i++) { //Update the Star. //Move the star towards the camera which is at 0 on Z. m_starZ[i] -= delta * m_speed; //If star is at or behind the camera, generate a new position for //it. if(m_starZ[i] <= 0) { InitStar(i); } //Render the Star. //Multiplying the position by (size/2) and then adding (size/2) //remaps the positions from range (-1, 1) to (0, size) //Division by z*tanHalfFOV moves things in to create a perspective effect. int x = (int)((m_starX[i]/(m_starZ[i] * tanHalfFOV)) * halfWidth + halfWidth); int y = (int)((m_starY[i]/(m_starZ[i] * tanHalfFOV)) * halfHeight + halfHeight); // // int x = (int)((m_starX[i]) * halfWidth + halfWidth); // int y = (int)((m_starY[i]) * halfHeight + halfHeight); //If the star is not within range of the screen, then generate a //new position for it. if(x < 0 || x >= target.GetWidth() || (y < 0 || y >= target.GetHeight())) { InitStar(i); continue; } // else // { // //Otherwise, it is safe to draw this star to the screen. // target.DrawPixel(x, y, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF); // } triangleBuilderCounter++; if(triangleBuilderCounter == 1) { x1 = x; y1 = y; } else if(triangleBuilderCounter == 2) { x2 = x; y2 = y; } else if(triangleBuilderCounter == 3) { triangleBuilderCounter = 0; // Vertex v1 = new Vertex(new Vector4f(x1/400.0f - 1.0f, y1/300.0f - 1.0f, 0.0f, 1.0f), // new Vector4f(1.0f, 0.0f, 0.0f, 0.0f)); // Vertex v2 = new Vertex(new Vector4f(x2/400.0f - 1.0f, y2/300.0f - 1.0f, 0.0f, 1.0f), // new Vector4f(1.0f, 1.0f, 0.0f, 0.0f)); // Vertex v3 = new Vertex(new Vector4f(x/400.0f - 1.0f, y/300.0f - 1.0f, 0.0f, 1.0f), // new Vector4f(0.0f, 1.0f, 0.0f, 0.0f)); // // target.DrawTriangle(v1, v2, v3, m_bitmap); } } } }
Transform.java
:
public class Transform { private Vector4f m_pos; private Quaternion m_rot; private Vector4f m_scale; public Transform() { this(new Vector4f(0,0,0,0)); } public Transform(Vector4f pos) { this(pos, new Quaternion(0,0,0,1), new Vector4f(1,1,1,1)); } public Transform(Vector4f pos, Quaternion rot, Vector4f scale) { m_pos = pos; m_rot = rot; m_scale = scale; } public Transform SetPos(Vector4f pos) { return new Transform(pos, m_rot, m_scale); } public Transform Rotate(Quaternion rotation) { return new Transform(m_pos, rotation.Mul(m_rot).Normalized(), m_scale); } public Transform LookAt(Vector4f point, Vector4f up) { return Rotate(GetLookAtRotation(point, up)); } public Quaternion GetLookAtRotation(Vector4f point, Vector4f up) { return new Quaternion(new Matrix4f().InitRotation(point.Sub(m_pos).Normalized(), up)); } public Matrix4f GetTransformation() { Matrix4f translationMatrix = new Matrix4f().InitTranslation(m_pos.GetX(), m_pos.GetY(), m_pos.GetZ()); Matrix4f rotationMatrix = m_rot.ToRotationMatrix(); Matrix4f scaleMatrix = new Matrix4f().InitScale(m_scale.GetX(), m_scale.GetY(), m_scale.GetZ()); return translationMatrix.Mul(rotationMatrix.Mul(scaleMatrix)); } public Vector4f GetTransformedPos() { return m_pos; } public Quaternion GetTransformedRot() { return m_rot; } public Vector4f GetPos() { return m_pos; } public Quaternion GetRot() { return m_rot; } public Vector4f GetScale() { return m_scale; } }
Vector4f.java
:
public class Vector4f { private final float x; private final float y; private final float z; private final float w; public Vector4f(float x, float y, float z, float w) { this.x = x; this.y = y; this.z = z; this.w = w; } public Vector4f(float x, float y, float z) { this(x, y, z, 1.0f); } public float Length() { return (float)Math.sqrt(x * x + y * y + z * z + w * w); } public float Max() { return Math.max(Math.max(x, y), Math.max(z, w)); } public float Dot(Vector4f r) { return x * r.GetX() + y * r.GetY() + z * r.GetZ() + w * r.GetW(); } public Vector4f Cross(Vector4f r) { float x_ = y * r.GetZ() - z * r.GetY(); float y_ = z * r.GetX() - x * r.GetZ(); float z_ = x * r.GetY() - y * r.GetX(); return new Vector4f(x_, y_, z_, 0); } public Vector4f Normalized() { float length = Length(); return new Vector4f(x / length, y / length, z / length, w / length); } public Vector4f Rotate(Vector4f axis, float angle) { float sinAngle = (float)Math.sin(-angle); float cosAngle = (float)Math.cos(-angle); return this.Cross(axis.Mul(sinAngle)).Add( //Rotation on local X (this.Mul(cosAngle)).Add( //Rotation on local Z axis.Mul(this.Dot(axis.Mul(1 - cosAngle))))); //Rotation on local Y } public Vector4f Rotate(Quaternion rotation) { Quaternion conjugate = rotation.Conjugate(); Quaternion w = rotation.Mul(this).Mul(conjugate); return new Vector4f(w.GetX(), w.GetY(), w.GetZ(), 1.0f); } public Vector4f Lerp(Vector4f dest, float lerpFactor) { return dest.Sub(this).Mul(lerpFactor).Add(this); } public Vector4f Add(Vector4f r) { return new Vector4f(x + r.GetX(), y + r.GetY(), z + r.GetZ(), w + r.GetW()); } public Vector4f Add(float r) { return new Vector4f(x + r, y + r, z + r, w + r); } public Vector4f Sub(Vector4f r) { return new Vector4f(x - r.GetX(), y - r.GetY(), z - r.GetZ(), w - r.GetW()); } public Vector4f Sub(float r) { return new Vector4f(x - r, y - r, z - r, w - r); } public Vector4f Mul(Vector4f r) { return new Vector4f(x * r.GetX(), y * r.GetY(), z * r.GetZ(), w * r.GetW()); } public Vector4f Mul(float r) { return new Vector4f(x * r, y * r, z * r, w * r); } public Vector4f Div(Vector4f r) { return new Vector4f(x / r.GetX(), y / r.GetY(), z / r.GetZ(), w / r.GetW()); } public Vector4f Div(float r) { return new Vector4f(x / r, y / r, z / r, w / r); } public Vector4f Abs() { return new Vector4f(Math.abs(x), Math.abs(y), Math.abs(z), Math.abs(w)); } public String toString() { return "(" + x + ", " + y + ", " + z + ", " + w + ")"; } public float GetX() { return x; } public float GetY() { return y; } public float GetZ() { return z; } public float GetW() { return w; } public boolean equals(Vector4f r) { return x == r.GetX() && y == r.GetY() && z == r.GetZ() && w == r.GetW(); } }
Vertex.java
:
public class Vertex { private Vector4f m_pos; private Vector4f m_texCoords; private Vector4f m_normal; /** Basic Getter */ public float GetX() { return m_pos.GetX(); } /** Basic Getter */ public float GetY() { return m_pos.GetY(); } public Vector4f GetPosition() { return m_pos; } public Vector4f GetTexCoords() { return m_texCoords; } public Vector4f GetNormal() { return m_normal; } /** * Creates a new Vertex in a usable state. */ public Vertex(Vector4f pos, Vector4f texCoords, Vector4f normal) { m_pos = pos; m_texCoords = texCoords; m_normal = normal; } public Vertex Transform(Matrix4f transform, Matrix4f normalTransform) { // The normalized here is important if you're doing scaling. return new Vertex(transform.Transform(m_pos), m_texCoords, normalTransform.Transform(m_normal).Normalized()); } public Vertex PerspectiveDivide() { return new Vertex(new Vector4f(m_pos.GetX()/m_pos.GetW(), m_pos.GetY()/m_pos.GetW(), m_pos.GetZ()/m_pos.GetW(), m_pos.GetW()), m_texCoords, m_normal); } public float TriangleAreaTimesTwo(Vertex b, Vertex c) { float x1 = b.GetX() - m_pos.GetX(); float y1 = b.GetY() - m_pos.GetY(); float x2 = c.GetX() - m_pos.GetX(); float y2 = c.GetY() - m_pos.GetY(); return (x1 * y2 - x2 * y1); } public Vertex Lerp(Vertex other, float lerpAmt) { return new Vertex( m_pos.Lerp(other.GetPosition(), lerpAmt), m_texCoords.Lerp(other.GetTexCoords(), lerpAmt), m_normal.Lerp(other.GetNormal(), lerpAmt) ); } public boolean IsInsideViewFrustum() { return Math.abs(m_pos.GetX()) <= Math.abs(m_pos.GetW()) && Math.abs(m_pos.GetY()) <= Math.abs(m_pos.GetW()) && Math.abs(m_pos.GetZ()) <= Math.abs(m_pos.GetW()); } public float Get(int index) { switch(index) { case 0: return m_pos.GetX(); case 1: return m_pos.GetY(); case 2: return m_pos.GetZ(); case 3: return m_pos.GetW(); default: throw new IndexOutOfBoundsException(); } } }
运行方式
- Open your preferred Java IDE, and create a new project.
- Import everything under the src folder as source files.
- Copy the res folder into your Java IDE’s folder for your project.
- Build and run
mv res obj/res cd obj java Main mv res ../res cd ../
艾孜尔江撰
2021年6月22日
这篇关于Java制作软光栅化渲染器_艾孜尔江撰的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23Springboot应用的多环境打包入门
- 2024-11-23Springboot应用的生产发布入门教程
- 2024-11-23Python编程入门指南
- 2024-11-23Java创业入门:从零开始的编程之旅
- 2024-11-23Java创业入门:新手必读的Java编程与创业指南
- 2024-11-23Java对接阿里云智能语音服务入门详解
- 2024-11-23Java对接阿里云智能语音服务入门教程
- 2024-11-23JAVA对接阿里云智能语音服务入门教程
- 2024-11-23Java副业入门:初学者的简单教程
- 2024-11-23JAVA副业入门:初学者的实战指南