3d测试:绕竖直轴旋转的几个三角形

这是一个用空间几何计算实现点的坐标变换、模拟视觉的例子。将2个java文件放在同一个包里。

主文件:


package com.baobao.sayhello;

import android.app.*;
import android.content.*;
import android.graphics.*;
import android.os.*;
import android.view.*;
import android.widget.*;
import java.util.*;


public class MainActivity extends Activity 
{

    mview view;
    int winx,winy,bmx,bmy,ct=8;
    Bitmap bm;
    Handler mHandler;
    ArrayList<tri3d.Triangle> t=new ArrayList<tri3d.Triangle>();
    tri3d.Point p=new tri3d.Point(0,-10,0);
    class mview extends View{//自定义一个叫mview的新类型,继承View
        public mview(Context con){//构造(初始化)函数
            super(con);
        }

        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh)
        {
            //窗口大小改变时触发
            super.onSizeChanged(w, h, oldw, oldh);
            winx=w;winy=h;bmx=winx/16;bmy=winy/16;
            Random r=new Random();
            int i;
            for(i=0;i<ct;i++)t.add(new tri3d.Triangle(
                new tri3d.Point(r.nextDouble()*4-2,r.nextDouble()*4-2,r.nextDouble()*4-2),
                new tri3d.Point(r.nextDouble()*4-2,r.nextDouble()*4-2,r.nextDouble()*4-2),
                new tri3d.Point(r.nextDouble()*4-2,r.nextDouble()*4-2,r.nextDouble()*4-2)
            ));
            bm=Bitmap.createBitmap(bmx,bmy,Bitmap.Config.RGB_565);//建立指定宽高的图片bm
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    int i;
                    for(i=0;i<t.size();i++){
                        t.set(i,t.get(i).rotate(new tri3d.Line(new tri3d.Point(0,0,0),new tri3d.Point(0,0,1)),0.2));
                    }
                    view.invalidate();//重绘窗口
                    removeMessages(1);
                    sendEmptyMessageDelayed(1, 100);//这里想几秒刷新一次就写几秒
                }
            };
            mHandler.sendEmptyMessage(1);
        
        }

        @Override
        protected void onDraw(Canvas canvas)
        {
            Paint paint=new Paint();
            int ix,iy,i,k;
            double min;
            ArrayList<tri3d.Base> b=new ArrayList<tri3d.Base>();
            for(i=0;i<t.size();i++)b.add(t.get(i).base());
            for(iy=0;iy<bmy;iy++)
                for(ix=0;ix<bmx;ix++){
                    min=1000;k=0;
                    ArrayList<ArrayList> ll=new ArrayList<ArrayList>();
                    tri3d.Point p1=new tri3d.Point((ix-bmx/2.0)*4/bmx,0,(bmy/2.0-iy)*6/bmy);
                    for(i=0;i<t.size();i++){
                        ArrayList l=new tri3d.Line(p,p.vectorTo(p1)).shootAt(b.get(i));
                        if((double)l.get(2)<min&&((tri3d.Point)l.get(1)).x>0&&((tri3d.Point)l.get(1)).y>0&&((tri3d.Point)l.get(1)).x+((tri3d.Point)l.get(1)).y<1){
                            min=l.get(2);
                            k=i;
                        }
                        ll.add(l);
                    }
                    tri3d.Point p2=(tri3d.Point)ll.get(k).get(1);
                    bm.setPixel(ix,iy,((boolean)ll.get(k).get(0)&&p2.x>0&&p2.y>0&&p2.x+p2.y<1)?Color.rgb((int)(p2.y*10)%2==0?255:127,(int)(255-((double)ll.get(k).get(2)-7)*256/6),0):Color.BLACK);//设置图片每个像素
                }
            canvas.drawBitmap(bm,null,new RectF(0,0,winx,winy),paint);//把图片贴到屏幕
        }
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            //触控屏幕时触发
            if(event.getAction()==MotionEvent.ACTION_DOWN){
                //如果事件类型为按下

            }

            return true;//返回真,表示已处理消息
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        view=new mview(this);//新建一个叫view的mview
        setContentView(view);//把view设为整个界面
    }
}

3d计算库:

package com.baobao.sayhello;
import java.util.*;

public class tri3d//空间几何计算库
{
    public static class Point{//点
        public double x,y,z;
        Point (double x,double y,double z){
            this.x=x;this.y=y;this.z=z;
        }
        public double length(){//和原点距离(长度)
            return Math.sqrt(x*x+y*y+z*z);
        }
        public Point unit(){//单位化
            return new Point(x/this.length(),y/this.length(),z/this.length());
        }
        public Point cross(Point p){//叉乘
            return new Point(y*p.z-z*p.y,z*p.x-x*p.z,x*p.y-y*p.x);
        }
        public Point translate(Point p){//平移(矢量和)
            return new Point(x+p.x,y+p.y,z+p.z);
        }
        public Point vectorTo(Point p){//由p1指向p2的向量
            return new Point(p.x-x,p.y-y,p.z-z);
        }
        public Point scale(double k){//以原点为中心缩放k倍
            return new Point(x*k,y*k,z*k);
        }
        public Point scale(Point p,double k){//以p为中心缩放k倍
            return p.translate(p.vectorTo(this).scale(k));
        }
        public Point rotate(Line l,double rad){//以l为轴右手向旋转角度rad
            Point n=l.p.unit();
            double sin=Math.sin(rad),cos=Math.cos(rad),cos1=1-cos;
            double ax=x-l.p0.x,ay=y-l.p0.y,az=z-l.p0.z;
            double cx=n.x*sin,cy=n.y*sin,cz=n.z*sin;
            double bxy=n.x*n.y*cos1,byz=n.y*n.z*cos1,bzx=n.z*n.x*cos1;
            return new Point(
                ax*(n.x*n.x*cos1+cos)+ay*(bxy-cz)+az*(bzx+cy)+l.p0.x,
                ay*(n.y*n.y*cos1+cos)+az*(byz-cx)+ax*(bxy+cz)+l.p0.y,
                az*(n.z*n.z*cos1+cos)+ax*(bzx-cy)+ay*(byz+cx)+l.p0.z
            );
        }
        public Point fromBase(Base b){//从b坐标系的坐标转为默认坐标系的坐标
            return b.p0.
            translate(b.px.scale(this.x)).
            translate(b.py.scale(this.y)).
            translate(b.pz.scale(this.z));
        }
        public Point toBase(Base b){//从默认坐标系的坐标转为b坐标系的坐标
            double p1x=b.px.x,p2x=b.py.x,p3x=b.pz.x;
            double p1y=b.px.y,p2y=b.py.y,p3y=b.pz.y;
            double p1z=b.px.z,p2z=b.py.z,p3z=b.pz.z;
            double p0x=x-b.p0.x,p0y=y-b.p0.y,p0z=z-b.p0.z;
            double d=p1x*(p2y*p3z-p3y*p2z)+p1y*(p2z*p3x-p2x*p3z)+p1z*(p2x*p3y-p2y*p3x);
            double dx=p0x*(p2y*p3z-p3y*p2z)+p0y*(p2z*p3x-p2x*p3z)+p0z*(p2x*p3y-p2y*p3x);
            double dy=p0x*(p1z*p3y-p3z*p1y)+p0y*(p1x*p3z-p3x*p1z)+p0z*(p1y*p3x-p3y*p1x);
            double dz=p0x*(p1y*p2z-p2y*p1z)+p0y*(p1z*p2x-p2z*p1x)+p0z*(p1x*p2y-p2x*p1y);
            return new Point(dx/d,dy/d,dz/d);
        }
    }
    public static class Line{//点向式直线
        public Point p0,p;
        Line (Point p0,Point p){
            this.p0=p0;this.p=p;
        }
        public Line toBase(Base b){//转到坐标系b的坐标
            Point p1=p0.toBase(b);
            return new Line(p1,p1.vectorTo(p0.translate(p).toBase(b)));
        }
        public ArrayList shootAt(Base b){//获得直线与t坐标系xoy面的交点(t坐标系),in为是否朝向平面
            Line l=this.toBase(b);
            ArrayList list=new ArrayList();
            list.add(l.p.z<0&&l.p0.z>0||l.p.z>0&&l.p0.z<0?true:false);
            list.add(l.p0.translate(l.p).scale(l.p0,-l.p0.z/l.p.z));
            list.add(p0.vectorTo(((Point)list.get(1)).fromBase(b)).length());
            return list;
        }
    }
    public static class Base{//坐标系
        public Point p0,px,py,pz;
        Base(Point p0,Point px,Point py,Point pz){
            this.p0=p0;this.px=px;this.py=py;this.pz=pz;
        }
    }
    public static class Triangle{//三角形
        public Point px,po,py;
        Triangle(Point px,Point po,Point py){
            this.px=px;this.po=po;this.py=py;
        }
        public Base base(){
            Point vx=po.vectorTo(px),vy=po.vectorTo(py);
            return new Base(po,vx,vy,vx.cross(vy));
        }
        public Triangle translate(Point p){
            return new Triangle(px.translate(p),po.translate(p),py.translate(p));
        }
        public Triangle scale(Point p,double k){
            return new Triangle(px.scale(p,k),po.scale(p,k),py.scale(p,k));
        }
        public Triangle rotate(Line l,double rad){
            return new Triangle(px.rotate(l,rad),po.rotate(l,rad),py.rotate(l,rad));
        }
    }
}

设置文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.baobao.sayhello" >

    <application
        android:allowBackup="true"
        android:icon="@drawable/baobao"
        android:label="天线宝宝"
        android:theme="@style/AppTheme"
        android:resizeableActivity = "true"
        android:hardwareAccelerated="false">
        <activity
            android:name=".MainActivity"
            android:label="天线宝宝说你好" 
            android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
效果

注意要把代码里的包名改成你的包名。

主文件里的bmx和bmy是绘制图片的宽高。图片越大质量越高但运行起来越卡。图片越小运行越流畅但接近马赛克。

这两个参数都是用屏幕宽高除以一个数计算的。可以改变除数值。除的越大图片宽和高就越小。

每个三角形三个顶点位置是随机的,每次运行一般不一样。

主要原理:

首先每一帧要计算出每个三角形所有点的坐标。
把眼睛当成一个点,从这个点向可视的方向引射线,就会和某些三角形内部相交。
由于遮挡关系,只能看到距离眼睛点最近的交点,根据这个交点在对应三角形平面上的位置来决定这个点是什么颜色。
对过眼睛点和每个屏幕像素点的射线,都枚举计算颜色,就能拼出一副3d图片。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容