• x01.EarthRun


    月亮绕着地球转,地球绕着太阳转,一个 TranslateTransform3D + RotationTransform3D 即可搞定。难的是球体(Sphere)绘制。

    作为球体,由经度、纬度划分为一小块一小块的。球体的绘制,说白了,就是这一小块一小块的绘制。除上下极点外,每小块由四个点(Point3D)构成,即两个三角形。

    现在,只要求出了这些 Point3D,问题即可解决。为简便,不妨设球心 O (0,0,0),半径 r1 。(WPF 3D 坐标:左到右 x 轴,下到上 y 轴,屏幕里到外 z 轴)

    如何求球面某经纬度交叉点 A (x,y,z)的 3D 坐标呢?

    1. 忽略 z 轴,设角 AOX (纬度偏移)度数为 a,则 x = cos(a); y = sin(a); 同理,z = cos(a);

    2. 设经度偏移角度 AOZ 为 b,此时,y 不变。忽略 y 轴,则 x = cos(a)*sin(b); z = cos(a)*cos(b);

    即:(cos(a)*sin(b), sin(a), cos(a)*cos(b)) 为 A 3D 坐标。参考代码如下:

    Trangulate
      protected void Triangulate(DependencyPropertyChangedEventArgs e,
                Point3DCollection vertices, Vector3DCollection normals,
                Int32Collection indices, PointCollection textures)
            {
                vertices.Clear();
                normals.Clear();
                indices.Clear();
                textures.Clear();
    
                for (int stack = 0; stack <= Stacks; stack++)
                {
                    double phi = Math.PI / 2 - stack * Math.PI / Stacks;
                    double y = Radius * Math.Sin(phi);
                    double scale = -Radius * Math.Cos(phi);
    
                    for (int slice = 0; slice <= Slices; slice++)
                    {
                        double theta = slice * 2 * Math.PI / Slices;
                        double x = scale * Math.Sin(theta);
                        double z = scale * Math.Cos(theta);
    
                        Vector3D normal = new Vector3D(x, y, z);
                        normals.Add(normal);
                        vertices.Add(normal + Center);
                        textures.Add(new Point((double)slice / Slices, (double)stack / Stacks));
                    }
                }
    
                // stack == 0
                for (int slice = 0; slice < Slices; slice++)
                {
                    indices.Add(slice + 1);
                    indices.Add(Slices + 1 + slice);
                    indices.Add(Slices + 1 + slice + 1);
                }
    
                for (int stack = 1; stack < Stacks - 1; stack++)
                {
                    for (int slice = 0; slice < Slices; slice++)
                    {
                        indices.Add(stack * (Slices + 1) + slice);
                        indices.Add((stack + 1) * (Slices + 1) + slice);
                        indices.Add(stack * (Slices + 1) + slice + 1);
    
                        indices.Add(stack * (Slices + 1) + slice + 1);
                        indices.Add((stack + 1) * (Slices + 1) + slice);
                        indices.Add((stack + 1) * (Slices + 1) + slice + 1);
                    }
                }
    
                // stack == Stacks - 1
                for (int slice = 0; slice < Slices; slice++)
                {
                    int stack = Stacks - 1;
                    indices.Add(stack * (Slices + 1) + slice);
                    indices.Add((stack + 1) * (Slices + 1) + slice);
                    indices.Add(stack * (Slices + 1) + slice + 1);
                }
            }

    其中,slice 即经度,stack 即纬度,phi 即 a, theta 即 b,它是从 -z 轴 0 到 360 度,+y 轴 90 到 -90 度。

    源代码可到 x01.download 中下载,运行效果图如下:

    关于 3D 编程,Charles Pezold 的 《3D.Programming.for.Windows》值得一读,下载链接 http://download.csdn.net/detail/china_x01/4816647 

  • 相关阅读:
    Shell——2
    Vim-快捷命令
    Shell——1
    linux命令笔记
    小飞机 + zeal 安装
    linux 环境下 假设被cc攻击,请从linux日志文件找出黑客ip地址
    日志文件例子
    最大子列和的四种方法,时间复杂度递减,直至为线性复杂度
    递归很耗内存+多项式求值的两种方法+c语言计时方法
    线代 第六章 二次型
  • 原文地址:https://www.cnblogs.com/china_x01/p/2798917.html
Copyright © 2020-2023  润新知