java 游戏编程 (八)

java3d设计的知识面比较多,常见的三维绘制有多边形造型和光束跟踪。
多边形造型(polygon modeling):将虚拟三维时间看做平面多边形。
光束跟踪(ray tracing):建立光束模型,简历眼到光源的模型。
java中常见的利用三维加速卡方法:Java3D和OpenGL关联。
Java3D核心是用OpenGL或DirectX绘制
现在我们用轻量级三维多边形绘制器,暂不用Java3D api
需要数学方面知识,但都很浅。
先考虑一个案例:我们构造一个四边形,然后它可以任意围绕x轴或y轴旋转,并且可以放大缩小。当然也可以围绕Z轴,只不过这样感觉不到3D效果。
解释一下这里的x,y,z。
在二维图形中,如果和屏幕相同的二维坐标,原点在屏幕左上方,x从左往右,y从上往下.
在三维图形中,x向右,y向上,z向里(感觉就是往屏幕里,远离人的方向)
我们从最终测试类出发,先构思出这个类应有的方法,然后回推该写的具体类和方法。
伪代码
package com.jsheng.game.test2;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;

import com.jsheng.game.util.GameAction;
import com.jsheng.game.util.GameCore;
import com.jsheng.game.util.InputManager;


/** function:
 * company: jsheng
 * @author wanghn      [email protected]
 */
public class  My3DTest1 extends GameCore {

    public static void main(String[] args) {
        new My3DTest1().run();
    }

    private 多边形 a = new 多边形();
    private GameAction exit = new GameAction("exit");
    private GameAction zoomIn = new GameAction("zoomIn");
    private GameAction zoomOut = new GameAction("zoomOut");

    public void init() {
        super.init();
        InputManager inputManager = new InputManager(
            screen.getFullScreenWindow());
        inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
        inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
        inputManager.mapToKey(zoomIn, KeyEvent.VK_UP);
        inputManager.mapToKey(zoomOut, KeyEvent.VK_DOWN);
    }

    public void do设置多边形(){
    	
    }

    public void do设置多边形旋转角度(){
    	
    }

    public void update(long elapsedTime) {
        if (exit.isPressed()) {
            stop();
            return;
        }
        elapsedTime = Math.min(elapsedTime, 100);
    }

    public void draw(Graphics2D g) {
    	//处理
        this.draw多边形(g, a);
    }

    /**
        在屏幕绘制多边形
    */
    private void draw多边形(Graphics2D g,多边形 a)
    {
        
    }
}

向量,三角函数等数学知识就不介绍了,这里的程序模拟了这些算法。

说个概念:视图窗口和镜头
视图窗口是三维空间中语屏幕窗口大小相同的窗口。镜头就好像眼睛,去观察视图窗口,然后去将三维物体投影到视图窗口。(这里可以参考相关资料,或我以后整理上图更好说明这个问题)
3D中的数学
将三维物体投影到视图窗口中可以简化为直角三角形问题。
假设三维物体坐标二维点是(x,y),投影到视图窗口的二维点是(x1,y1)
从镜头到视图窗口距离是d
三维点到原点距离是z,得到:
x1=dx/-z;y1=dy/-z
d的值是我们来设置的
另外还能用夹角
从镜头出发到视图窗口两遍发射直线,两直线得到一个夹角,我们就可以通过三角函数来计算一些问题。
给出类代码,具备视图窗口和投影功能。
package com.jsheng.game.util.java3d;

import java.awt.Rectangle;
public class ViewWindow {

    private Rectangle bounds;			//视图窗口的边界
    private float angle;				//水平视角
    private float distance;  	//视图窗口到镜头的距离

    public ViewWindow(int left, int top, int width, int height,
        float angle)
    {
        bounds = new Rectangle();
        this.angle = angle;
        setBounds(left, top, width, height);
    }

    public void setBounds(int left, int top, int width,
        int height)
    {
        bounds.x = left;
        bounds.y = top;
        bounds.width = width;
        bounds.height = height;
        distance = (bounds.width/2) /
            (float)Math.tan(angle/2);
    }

    public void setAngle(float angle) {
        this.angle = angle;
        distance = (bounds.width/2) /
            (float)Math.tan(angle/2);
    }
    public float getAngle() {
        return angle;
    }

    public int getWidth() {
        return bounds.width;
    }


    public int getHeight() {
        return bounds.height;
    }

    public int getTopOffset() {
        return bounds.y;
    }

    public int getLeftOffset() {
        return bounds.x;
    }

    public float getDistance() {
        return distance;
    }



    public float convertFromViewXToScreenX(float x) {
        return x + bounds.x + bounds.width/2;
    }


    public float convertFromViewYToScreenY(float y) {
        return -y + bounds.y + bounds.height/2;
    }

    public float convertFromScreenXToViewX(float x) {
        return x - bounds.x - bounds.width/2;
    }

    public float convertFromScreenYToViewY(float y) {
        return -y + bounds.y + bounds.height/2;
    }

    //将指定的向量投影到屏幕
    public void project(Vector3D v) {
        v.x = distance * v.x / -v.z;
        v.y = distance * v.y / -v.z;

        // convert to screen coordinates
        v.x = convertFromViewXToScreenX(v.x);
        v.y = convertFromViewYToScreenY(v.y);
    }
}
]

处理多边形:
package com.jsheng.game.util.java3d;

public class Polygon3D {

    private static Vector3D temp1 = new Vector3D();
    private static Vector3D temp2 = new Vector3D();

    private Vector3D[] v;
    private int numVertices;
    private Vector3D normal;

    public Polygon3D() {
        numVertices = 0;
        v = new Vector3D[0];
        normal = new Vector3D();
    }


    public Polygon3D(Vector3D[] v) {
        this.v = v;
        numVertices = v.length;
        calcNormal();
    }

    public void setTo(Polygon3D polygon) {
        numVertices = polygon.numVertices;
        normal.setTo(polygon.normal);

        ensureCapacity(numVertices);
        for (int i=0; i<numVertices; i++) {
            v[i].setTo(polygon.v[i]);
        }
    }

    public void ensureCapacity(int length) {
        if (v.length < length) {
            Vector3D[] newV = new Vector3D[length];
            for (int i=v.length; i<newV.length; i++) {
                newV[i] = new Vector3D();
            }
            v = newV;
        
        }
    }

    public int getNumVertices() {
        return numVertices;
    }

    public Vector3D getVertex(int index) {
        return v[index];
    }

    public void project(ViewWindow view) {
        for (int i=0; i<numVertices; i++) {
            view.project(v[i]);
        }
    }

    public void add(Vector3D u) {
       for (int i=0; i<numVertices; i++) {
           v[i].add(u);
       }
    }

    public void subtract(Vector3D u) {
       for (int i=0; i<numVertices; i++) {
           v[i].subtract(u);
       }
    }

    public void add(Transform3D xform) {
        addRotation(xform);
        add(xform.getLocation());
    }

    public void subtract(Transform3D xform) {
        subtract(xform.getLocation());
        subtractRotation(xform);
    }

    public void addRotation(Transform3D xform) {
        for (int i=0; i<numVertices; i++) {
           v[i].addRotation(xform);
        }
        normal.addRotation(xform);
    }

    public void subtractRotation(Transform3D xform) {
        for (int i=0; i<numVertices; i++) {
           v[i].subtractRotation(xform);
        }
        normal.subtractRotation(xform);
    }



    public Vector3D calcNormal() {
        if (normal == null) {
            normal = new Vector3D();
        }
        temp1.setTo(v[2]);
        temp1.subtract(v[1]);
        temp2.setTo(v[0]);
        temp2.subtract(v[1]);
        normal.setToCrossProduct(temp1, temp2);
        normal.normalize();
        return normal;
    }

    public Vector3D getNormal() {
        return normal;
    }

    public void setNormal(Vector3D n) {
        if (normal == null) {
            normal = new Vector3D(n);
        }
        else {
            normal.setTo(n);
        }
    }

    public boolean isFacing(Vector3D u) {
        temp1.setTo(u);
        temp1.subtract(v[0]);
        return (normal.getDotProduct(temp1) >= 0);
    }


}

三维变换之旋转:
譬如沿z轴旋转,则z值不变。
r是旋转半径
则x = rcos(a)
y=rcos(a)
a是原始角度
x1=rcos(a+b)
y1=rsin(a+b)
b是旋转角度
x1=rcosacosb-rsinasinb
y1=rsinacosb+rsinbcosa

最后得到
x1=xcosb-ysinb
y1=xsinb+ycosb
开发包装旋转功能的类
package com.jsheng.game.util.java3d;

public class Transform3D {

    protected Vector3D location;
    private float cosAngleX;
    private float sinAngleX;
    private float cosAngleY;
    private float sinAngleY;
    private float cosAngleZ;
    private float sinAngleZ;

    public Transform3D() {
        this(0,0,0);
    }

    public Transform3D(float x, float y, float z) {
        location = new Vector3D(x, y, z);
        setAngle(0,0,0);
    }

    public Transform3D(Transform3D v) {
        location = new Vector3D();
        setTo(v);
    }


    public Object clone() {
        return new Transform3D(this);
    }


    public void setTo(Transform3D v) {
        location.setTo(v.location);
        this.cosAngleX = v.cosAngleX;
        this.sinAngleX = v.sinAngleX;
        this.cosAngleY = v.cosAngleY;
        this.sinAngleY = v.sinAngleY;
        this.cosAngleZ = v.cosAngleZ;
        this.sinAngleZ = v.sinAngleZ;
    }


    public Vector3D getLocation() {
        return location;
    }

    public float getCosAngleX() {
        return cosAngleX;
    }

    public float getSinAngleX() {
        return sinAngleX;
    }

    public float getCosAngleY() {
        return cosAngleY;
    }

    public float getSinAngleY() {
        return sinAngleY;
    }

    public float getCosAngleZ() {
        return cosAngleZ;
    }

    public float getSinAngleZ() {
        return sinAngleZ;
    }

    public float getAngleX() {
        return (float)Math.atan2(sinAngleX, cosAngleX);
    }

    public float getAngleY() {
        return (float)Math.atan2(sinAngleY, cosAngleY);
    }

    public float getAngleZ() {
        return (float)Math.atan2(sinAngleZ, cosAngleZ);
    }

    public void setAngleX(float angleX) {
        cosAngleX = (float)Math.cos(angleX);
        sinAngleX = (float)Math.sin(angleX);
    }

    public void setAngleY(float angleY) {
        cosAngleY = (float)Math.cos(angleY);
        sinAngleY = (float)Math.sin(angleY);
    }

    public void setAngleZ(float angleZ) {
        cosAngleZ = (float)Math.cos(angleZ);
        sinAngleZ = (float)Math.sin(angleZ);
    }

    public void setAngle(float angleX, float angleY, float angleZ)
    {
        setAngleX(angleX);
        setAngleY(angleY);
        setAngleZ(angleZ);
    }

    public void rotateAngleX(float angle) {
        if (angle != 0) {
            setAngleX(getAngleX() + angle);
        }
    }

    public void rotateAngleY(float angle) {
        if (angle != 0) {
            setAngleY(getAngleY() + angle);
        }
    }

    public void rotateAngleZ(float angle) {
        if (angle != 0) {
            setAngleZ(getAngleZ() + angle);
        }
    }

    public void rotateAngle(float angleX, float angleY,
        float angleZ)
    {
        rotateAngleX(angleX);
        rotateAngleY(angleY);
        rotateAngleZ(angleZ);
    }

}



最后完成最初设想的类
package com.jsheng.game.test2;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.geom.GeneralPath;

import com.jsheng.game.util.GameAction;
import com.jsheng.game.util.GameCore;
import com.jsheng.game.util.InputManager;
import com.jsheng.game.util.java3d.Polygon3D;
import com.jsheng.game.util.java3d.Transform3D;
import com.jsheng.game.util.java3d.Vector3D;
import com.jsheng.game.util.java3d.ViewWindow;

/** function:
 * company: jsheng
 * @author wanghn      [email protected]
 */
public class My3DTest1 extends GameCore {

    public static void main(String[] args) {
        new My3DTest1().run();
    }
    Vector3D v1 =  new Vector3D(-50, 0, 0);
    Vector3D v2 = new Vector3D(50, 0, 0);
    Vector3D v3 = new Vector3D(-50, 100, 0);
    Vector3D v4 = new Vector3D(50, 100, 0);
    Vector3D[] vs = new Vector3D[]{v1,v2,v3,v4};
    private Polygon3D p = new Polygon3D(vs);


    private Transform3D myTransform = new Transform3D(0,0,-500);
    private Polygon3D transformedPolygon = new Polygon3D();
    private ViewWindow viewWindow;

    private GameAction exit = new GameAction("exit");
    private GameAction zoomIn = new GameAction("zoomIn");
    private GameAction zoomOut = new GameAction("zoomOut");

    public void init() {
        super.init();
        InputManager inputManager = new InputManager(
            screen.getFullScreenWindow());
        inputManager.setCursor(InputManager.INVISIBLE_CURSOR);
        inputManager.mapToKey(exit, KeyEvent.VK_ESCAPE);
        inputManager.mapToKey(zoomIn, KeyEvent.VK_UP);
        inputManager.mapToKey(zoomOut, KeyEvent.VK_DOWN);
        viewWindow = new ViewWindow(0, 0,
            screen.getWidth(), screen.getHeight(),
            (float)Math.toRadians(75));
    }

    public void update(long elapsedTime) {
        if (exit.isPressed()) {
            stop();
            return;
        }
        elapsedTime = Math.min(elapsedTime, 100);
        myTransform.rotateAngleY(0.002f*elapsedTime);
        if (zoomIn.isPressed()) {
        	myTransform.getLocation().z += 0.5f*elapsedTime;
        }
        if (zoomOut.isPressed()) {
        	myTransform.getLocation().z -= 0.5f*elapsedTime;
        }
    }

    public void draw(Graphics2D g) {
        g.setColor(Color.black);
        g.fillRect(0, 0, screen.getWidth(), screen.getHeight());
        g.setColor(Color.white);
        g.drawString("按下ESC退出",
            5, fontSize);
        trandformAndDraw(g, p);
    }


    private void trandformAndDraw(Graphics2D g,
    		Polygon3D poly)
    {
        transformedPolygon.setTo(poly);
        transformedPolygon.add(myTransform);
        transformedPolygon.project(viewWindow);
        GeneralPath path = new GeneralPath();
        Vector3D v = transformedPolygon.getVertex(0);
        path.moveTo(v.x, v.y);
        for (int i=1; i<transformedPolygon.getNumVertices(); i++) {
            v = transformedPolygon.getVertex(i);
            path.lineTo(v.x, v.y);
        }
        g.setColor(Color.red);
        g.fill(path);
    }
}

你可能感兴趣的:(java,游戏,编程,算法,UP)