cad opengl 旋转 鼠标_对网格进行平移,缩放,旋转

本文介绍了如何使用OpenGL结合鼠标监听实现对网格的平移、缩放和旋转操作。通过MouseMotionListener、MouseListener和MouseWheelListener监听鼠标动作,利用glTranslatef、glScalef和glRotatef进行图形变换。平移根据鼠标偏移量进行,缩放依据滚轮动作调整比例,旋转则借助Arcball算法和四元数来完成三维旋转。最后,文章提到了Arcball和四元数的相关资源。
摘要由CSDN通过智能技术生成

e7590c4dc4714026b72ea316698f2102.png

查看网格时可能想要进行一些操作,比如想通过鼠标对图像进行平移,旋转,缩放,这时就要增加一些内容。

为了获得鼠标动作,GlCanvas需要增加几个鼠标监听:

  • MouseMotionListener:负责追踪鼠标的运动位置
  • MouseListener:负责鼠标的按,释放,点击等动作
  • MouseWheelListener :负责接收鼠标的滚轮动作

OpenGL中提供了了各种图形变换操作:
平移:glTranslatef(float f, float f1, float f2)
缩放: glScalef(float f0, float f1, float f2)
旋转: glRotatef(float a, float x, float y, float z) 或 glMultMatrixf(float[] floats, int i)

这样,网格的操作可以按下面方式实现:

平移:

  1. 从鼠标监听器的mouseDragged(MouseEvent e)获得鼠标(在屏幕空间)的x,y偏移量
  2. 按比例计算物体的xoffset, yoffset偏移量
  3. 调用gl.glTranslated(xoffset, yoffset, 0), 完成。

缩放:

  1. 从鼠标监听器的mouseWheelMoved(MouseWheelEvent e)获得鼠标滚轮动作
  2. 计算物体的缩放量,比如滚一下缩小至90%
  3. 调用glScalef(0.9f,0.9f,0.9f), 完成。

旋转:

旋转要稍微复杂一点。

第一个问题,就是如何将鼠标的二维移动换算成三维物体的旋转。因为屏幕是二维的平面,而我们要旋转的物体是三维的。

比较好的方法是用Arcball。Arcball的思路是假想在屏幕后方有个球心位置固定的圆球,你在用鼠标拖着这个大球旋转。鼠标动作按下图方法换算成大球的旋转轴和旋转角度,接着再换算成对应的四元数以及旋转矩阵。

921f6fb453a033c064e2cf9227253689.png
Arcball: 鼠标从屏幕A点移动到B点, 先求交点C, 再计算旋转角度和旋转轴矢量

Opengl里处理旋转,可以用glRotatef 或者glMultMatrixf。

glRotatef(float a, float x, float y, float z) 需要给定旋转轴(x,y,z)和角度a,程序会算出对应的旋转矩阵,和当前矩阵相乘后模型就得到了旋转。glMultMatrixf则是直接和输入的矩阵相乘。

glMultMatrixf需要手动计算旋转矩阵。处理三维旋转矩阵比较好的方式是用四元数来处理。

四元数是一种高阶复数,四元数q表示为:q=xi+yj+zk+w。 给定一个单位长度的旋转轴(x, y, z)和一个角度θ。对应的四元数为:q=((x,y,z)sinθ/2, cosθ/2) 。这样一个三维旋转通过一个四元数q就能表示。 有关四元数它和对应的旋转矩阵,可参考下面的网页:

Maths - Conversion Quaternion to Matrix​www.euclideanspace.com

网上有很多关于arcball和四元数的代码,可以随便拿来用。文末的代码中,Arcball定义旋转球,Quat4f定义四元数,Matrix4f处理四元数对应的旋转矩阵。

最终的效果如下:

4b6ec2ff03143af7ff8a167cf4c67560.png

3cd2114ad324d037725929c660dfd01a.png

Arcball:

import java.awt.Point;

class ArcBall {
    
    private static final float Epsilon = 1.0e-5f;
 
    Vector3f StVec;          //Saved click vector
    Vector3f EnVec;          //Saved drag vector
    float adjustWidth;       //Mouse bounds width
    float adjustHeight;      //Mouse bounds height
 
    public ArcBall(float NewWidth, float NewHeight) {
        StVec = new Vector3f();
        EnVec = new Vector3f();
        setBounds(NewWidth, NewHeight);
    }
 
    public void mapToSphere(Point point, Vector3f vector) {
        // Copy paramter into temp point
        Point2f tempPoint = new Point2f(point.x, point.y);
 
        // Adjust point coords and scale down to range of [-1 ... 1]
        tempPoint.x = (tempPoint.x * this.adjustWidth) - 1.0f;
        tempPoint.y = 1.0f - (tempPoint.y * this.adjustHeight);
 
        // Compute the square of the length of the vector to the point from the center
        float length = (tempPoint.x * tempPoint.x) + (tempPoint.y * tempPoint.y);
 
        // If the point is mapped outside of the sphere... (length > radius squared)
        if (length > 1.0f) {
            // Compute a normalizing factor (radius / sqrt(length))
            float norm = (float) (1.0 / Math.sqrt(length));
 
            // Return the "normalized" vector, a point on the sphere
            vector.x = tempPoint.x * norm;
            vector.y = tempPoint.y * norm;
            vector.z = 0.0f;
        } else    //Else it's on the inside
        {
            // Return a vector to a point mapped inside the sphere 
            // sqrt(radius squared - length)
            vector.x = tempPoint.x;
            vector.y = tempPoint.y;
            vector.z = (float) Math.sqrt(1.0f - length);
        }
 
    }
 
    public void setBounds(float NewWidth, float NewHeight) {
        assert((NewWidth > 1.0f) && (NewHeight > 1.0f));
 
        // Set adjustment factor for width/height
        adjustWidth = 1.0f / ((NewWidth - 1.0f) * 0.5f);
        adjustHeight = 1.0f / ((NewHeight - 1.0f) * 0.5f);
    }
 
    // Mouse down
    public void click(Point NewPt) {
        mapToSphere(NewPt, this.StVec);
 
    }
 
    // Mouse drag, calculate rotation
    public void drag(Point NewPt, Quat4f NewRot) {
        // Map the point to the sphere
        this.mapToSphere(NewPt, EnVec);
 
        // Return the quaternion equivalent to the rotation
        if (NewRot != null) {
            Vector3f Perp = new Vector3f();
 
            // Compute the vector perpendicular to the begin and end vectors
            Vector3f.cross(Perp, StVec, EnVec);
 
            // Compute the length of the perpendicular vector
            if (Perp.length() > Epsilon)    //if its non-zero
            {
                // We're ok, so return the perpendicular vector as the transform 
                // after all
                NewRot.x = Perp.x;
                NewRot.y = Perp.y;
                NewRot.z = Perp.z;
                // In the quaternion values, w is cosine (theta / 2), 
                // where theta is rotation angle
                NewRot.w = Vector3f.dot(StVec, EnVec);
                //System.out.println("w is "+NewRot.w);
            } else                                    //if its zero
            {
                // The begin and end vectors coincide, so return an identity transform
                NewRot.x = NewRot.y = NewRot.z = NewRot.w = 0.0f;
            }
        }
    }

 class Vector3f {
    public float x, y, z;
 
    public static void cross(Vector3f Result, Vector3f v1, Vector3f v2) {
        Result.x = (v1.y * v2.z) - (v1.z * v2.y);
        Result.y = (v1.z * v2.x) - (v1.x * v2.z);
        Result.z = (v1.x * v2.y) - (v1.y * v2.x);
    }
 
    public static float dot(Vector3f v1, Vector3f v2) {
        return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z + v2.z);
    }
 
    public float length() {
        return (float)Math.sqrt(x * x + y * y + z * z);
    }
}

四元数:

class Quat4f {
    public float x, y, z, w;
}

class Matrix4f {
    float M00;
    float M10;
    float M20;
    float M30;
    float M01;
    float M11;
    float M21;
    float M31;
    float M02;
    float M12;
    float M22;
    float M32;
    float M03;
    float M13;
    float M23;
    float M33;
 
    public Matrix4f() {
        setIdentity();
    }
 
    void get(float[] dest) {
        dest[0] = M00;
        dest[1] = M10;
        dest[2] = M20;
        dest[3] = M30;
        dest[4] = M01;
        dest[5] = M11;
        dest[6] = M21;
        dest[7] = M31;
        dest[8] = M02;
        dest[9] = M12;
        dest[10] = M22;
        dest[11] = M32;
        dest[12] = M03;
        dest[13] = M13;
        dest[14] = M23;
        dest[15] = M33;
    }
 
    void setZero() {
        M00 = M01 = M02 = M03 = M10 = M11 = M12 = M13 = M20 = M21 = M22 = 
                M23 = M30 = M31 = M32 = M33 = 0.0f;
    }
 
    void setIdentity() {
        setZero();
        M00 = M11 = M22 = M33 = 1.0f;
    }
 
    void setRotation(Quat4f q1) {
        float n, s;
        float xs, ys, zs;
        float wx, wy, wz;
        float xx, xy, xz;
        float yy, yz, zz;
 
        n = (q1.x * q1.x) + (q1.y * q1.y) + (q1.z * q1.z) + (q1.w * q1.w);
        s = (n > 0.0f) ? (2.0f / n) : 0.0f;
 
        xs = q1.x * s;
        ys = q1.y * s;
        zs = q1.z * s;
        wx = q1.w * xs;
        wy = q1.w * ys;
        wz = q1.w * zs;
        xx = q1.x * xs;
        xy = q1.x * ys;
        xz = q1.x * zs;
        yy = q1.y * ys;
        yz = q1.y * zs;
        zz = q1.z * zs;
 
        M00 = 1.0f - (yy + zz);
        M01 = xy - wz;
        M02 = xz + wy;
        M03 = 0f;
 
        M10 = xy + wz;
        M11 = 1.0f - (xx + zz);
        M12 = yz - wx;
        M13 = 0f;
        M20 = xz - wy;
        M21 = yz + wx;
        M22 = 1.0f - (xx + yy);
        M23 = 0f;
        M30 = 0f;
        M31 = 0f;
        M32 = 0f;
        M33 = 1f;
    }
 
    public final void set(Matrix4f m1) {
        M00 = m1.M00; M01 = m1.M01; M02 = m1.M02; M03 = m1.M03;
        M10 = m1.M10; M11 = m1.M11; M12 = m1.M12; M13 = m1.M13;
        M20 = m1.M20; M21 = m1.M21; M22 = m1.M22; M23 = m1.M23;
        M30 = m1.M30; M31 = m1.M31; M32 = m1.M32; M33 = m1.M33;
    }
 
    /**
     * Sets the value of this matrix to the result of multiplying
     * the two argument matrices together.
     *
     * @param m1 the first matrix
     * @param m2 the second matrix
     */
    public final void mul(Matrix4f m1, Matrix4f m2) {
        // alias-safe way.
        set(
                m1.M00 * m2.M00 + m1.M01 * m2.M10 + m1.M02 * m2.M20 + m1.M03 * m2.M30,
                m1.M00 * m2.M01 + m1.M01 * m2.M11 + m1.M02 * m2.M21 + m1.M03 * m2.M31,
                m1.M00 * m2.M02 + m1.M01 * m2.M12 + m1.M02 * m2.M22 + m1.M03 * m2.M32,
                m1.M00 * m2.M03 + m1.M01 * m2.M13 + m1.M02 * m2.M23 + m1.M03 * m2.M33,
 
                m1.M10 * m2.M00 + m1.M11 * m2.M10 + m1.M12 * m2.M20 + m1.M13 * m2.M30,
                m1.M10 * m2.M01 + m1.M11 * m2.M11 + m1.M12 * m2.M21 + m1.M13 * m2.M31,
                m1.M10 * m2.M02 + m1.M11 * m2.M12 + m1.M12 * m2.M22 + m1.M13 * m2.M32,
                m1.M10 * m2.M03 + m1.M11 * m2.M13 + m1.M12 * m2.M23 + m1.M13 * m2.M33,
 
                m1.M20 * m2.M00 + m1.M21 * m2.M10 + m1.M22 * m2.M20 + m1.M23 * m2.M30,
                m1.M20 * m2.M01 + m1.M21 * m2.M11 + m1.M22 * m2.M21 + m1.M23 * m2.M31,
                m1.M20 * m2.M02 + m1.M21 * m2.M12 + m1.M22 * m2.M22 + m1.M23 * m2.M32,
                m1.M20 * m2.M03 + m1.M21 * m2.M13 + m1.M22 * m2.M23 + m1.M23 * m2.M33,
 
                m1.M30 * m2.M00 + m1.M31 * m2.M10 + m1.M32 * m2.M20 + m1.M33 * m2.M30,
                m1.M30 * m2.M01 + m1.M31 * m2.M11 + m1.M32 * m2.M21 + m1.M33 * m2.M31,
                m1.M30 * m2.M02 + m1.M31 * m2.M12 + m1.M32 * m2.M22 + m1.M33 * m2.M32,
                m1.M30 * m2.M03 + m1.M31 * m2.M13 + m1.M32 * m2.M23 + m1.M33 * m2.M33
        );
    }
 
    /**
     * Sets 16 values
     */
    private void set(float m00, float m01, float m02, float m03,
                     float m10, float m11, float m12, float m13,
                     float m20, float m21, float m22, float m23,
                     float m30, float m31, float m32, float m33) {
        this.M00 = m00;
        this.M01 = m01;
        this.M02 = m02;
        this.M03 = m03;
        this.M10 = m10;
        this.M11 = m11;
        this.M12 = m12;
        this.M13 = m13;
        this.M20 = m20;
        this.M21 = m21;
        this.M22 = m22;
        this.M23 = m23;
        this.M30 = m30;
        this.M31 = m31;
        this.M32 = m32;
        this.M33 = m33;
    }
    
    public void print(){
        System.out.println(M00+","+M01+","+M02+","+M03);
        System.out.println(M10+","+M11+","+M12+","+M13);
        System.out.println(M20+","+M21+","+M22+","+M23);
        System.out.println(M30+","+M31+","+M32+","+M33);
    }     
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值