Android OpenGL 坐标系 <2>

本文介绍了Android中OpenGL的3D坐标系,原点位于屏幕中心,Z轴指向屏幕外,而X-Y平面在屏幕内部。为使线条可见,需要将坐标系沿Z轴负方向移动。此外,还提到了纹理坐标系和如何通过调整颜色实现颜色渐变。通过示例代码展示了如何绘制OpenGL坐标系,并通过旋转实现Z轴的可视化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机中任何绘图都离不开坐标系只是在每种绘图中坐标系形式有些差异,比如平时做android的View,在自定义View中绘制直线,那么这个时候默认的是采用的是屏幕坐标系,即坐标原点在屏幕左上角,X轴向右,Y向下:

而在opengl中采用的是3维坐标:


坐标原点默认在屏幕的中间,即(width/2,height/2)位置上,z轴是从屏幕"内"指向屏幕外,而且还要注意原点和x,y轴平面是在屏幕的"表面",有引号,这个"表面"刚好是人看不到的面,所以如果你要画一条线,线端点(1,0,0),(0,1,0),直接画到屏幕上,将看不到显示,怎么办?很简单,这个坐标系是三维空间的,那么就将坐标系沿Z轴的负轴方向移动一点,将X,Y平面稍微向屏幕内部移动一点,就能够看到直线了,想象一下在三维空间中,将坐标系往屏幕里面推一下,那么x,y轴形成的平面就在屏幕里面了,在它上面的直线就可以看见了.后面有例子.


纹理坐标系:

一般坐标系都自由操作移动(或旋转).


基本了解了坐标系,后面就可以开始绘图了

根据上面的可以通过画直线画出OPENGL坐标系,这样就可以被看见了:

<1> : android studio新建PumpKinBasicGL10工程:程序如下:

package org.durian.pumpkinbasicgl10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import org.durian.pumpkinbasicgl10.draw2d.PumpKinRenderer;


public class MainActivity extends Activity {

    private GLSurfaceView mSurfaceView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mSurfaceView=new GLSurfaceView(this);
        mSurfaceView.setRenderer(new PumpKinRenderer()/*new PumpkinDotRenderer()*//*new PumpKinLineRenderer(this)*//*new PumpKinTriangleRenderer()*//*new PumpKinPyramidRenderer()*/);
        setContentView(mSurfaceView/*R.layout.activity_main*/);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mSurfaceView.onResume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mSurfaceView.onPause();
    }
}

渲染类:

</pre><pre class="html" name="code">package org.durian.pumpkinbasicgl10.draw2d;

import android.opengl.GLSurfaceView;
import android.opengl.GLU;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Administrator on 2016/4/28.
 */
public class PumpKinRenderer implements GLSurfaceView.Renderer {

    private PumpKin pumpKin;

    public PumpKinRenderer(){
        pumpKin=new PumpKin();
    }
    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        gl.glClearColor(0.0f,0.0f,0.0f,1.0f);
        gl.glClearDepthf(1.0f);
        gl.glEnable(GL10.GL_DEPTH_TEST);
        gl.glDepthFunc(GL10.GL_LEQUAL);
        gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,GL10.GL_NICEST);
        gl.glShadeModel(GL10.GL_SMOOTH);
        gl.glDisable(GL10.GL_DITHER);

    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        if(height==0){
            height=1;
        }
        float aspect=(float)width/height;

        gl.glViewport(0,0,width,height);

        gl.glMatrixMode(GL10.GL_PROJECTION);
        gl.glLoadIdentity();
        GLU.gluPerspective(gl, 45, aspect, 0.1f, 100.0f);

        gl.glMatrixMode(GL10.GL_MODELVIEW);
        gl.glLoadIdentity();

    }

    @Override
    public void onDrawFrame(GL10 gl) {

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

        gl.glLoadIdentity();
        gl.glTranslatef(0.0f,0.0f,-2f);
        //gl.glRotatef(45,1.0f,1.0f,1.0f);
        pumpKin.draw(gl);

    }
}

这个渲染类里面稍微注意将坐标系"下沉"-2f单位.这样方便看到图形.可以试一下不执行:

gl.glTranslatef(0.0f,0.0f,-2f);
就能够体会了.

绘图类:

package org.durian.pumpkinbasicgl10.draw2d;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.opengles.GL10;

/**
 * Created by Administrator on 2016/4/28.
 */
public class PumpKin {

    private FloatBuffer vertexsBuffer;
    private FloatBuffer colorsBuffer;

    private ByteBuffer indicesBuffer;

    private float vertexs[]={
        0.0f,0.0f,0.0f,
            1.0f,0.0f,0.0f,
            0.0f,0.0f,0.0f,
            0.0f,1.0f,0.0f,
            0.0f,0.0f,0.0f,
            0.0f,0.0f,1.0f
    };

    private float colors[]={
            1.0f,0.0f,0.0f,1.0f,
            1.0f,0.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f
    };

    private byte indices[]={0,1,2};

    public PumpKin(){

        ByteBuffer vbb=ByteBuffer.allocateDirect(vertexs.length*4);
        vbb.order(ByteOrder.nativeOrder());
        vertexsBuffer=vbb.asFloatBuffer();
        vertexsBuffer.put(vertexs);
        vertexsBuffer.position(0);

        ByteBuffer cbb=ByteBuffer.allocateDirect(colors.length*4);
        cbb.order(ByteOrder.nativeOrder());
        colorsBuffer=cbb.asFloatBuffer();
        colorsBuffer.put(colors);
        colorsBuffer.position(0);

    }

    public void draw(GL10 gl){

        gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl.glEnableClientState(GL10.GL_COLOR_ARRAY);

        gl.glVertexPointer(3,GL10.GL_FLOAT,0,vertexsBuffer);
        gl.glColorPointer(4,GL10.GL_FLOAT,0,colorsBuffer);

        gl.glDrawArrays(GL10.GL_LINES,0,vertexs.length/3);

        gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);

    }

}


给线添加颜色:

private float colors[]={
            1.0f,0.0f,0.0f,1.0f,
            1.0f,0.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f,
            0.0f,0.0f,1.0f,1.0f
    };


colors[0]和colors[1]表示从原点到坐标(1,0,0)的颜色,如果[0]和[1]颜色不同,将会产生颜色渐变.

如果加入上面数组改为:

private float colors[]={
            1.0f,0.0f,0.0f,1.0f,
            1.0f,0.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f,
            0.0f,1.0f,0.0f,1.0f
    };

试一下,画Z会是什么颜色?

将会是(0.0f,1.0f,0.0f,1.0f),即绿色.

运行结果:

发现只有两个坐标,Z轴没有发现,其实Z轴目前正垂直于屏幕,指向屏幕外,所以看不见,可以修改程序,让坐标旋转一下:

@Override
    public void onDrawFrame(GL10 gl) {

        gl.glClear(GL10.GL_COLOR_BUFFER_BIT|GL10.GL_DEPTH_BUFFER_BIT);

        gl.glLoadIdentity();
        gl.glTranslatef(0.0f,0.0f,-2f);
        gl.glRotatef(45,1.0f,1.0f,1.0f);
        pumpKin.draw(gl);

    }

沿着矢量(1.0f,1.0f,1.0f)做右手定则式旋转,即手握住这个矢量方向,顺手指的方向旋转45度.也可以说做了矩阵变换后得到的.


途中虚线是截图的问题,实际屏幕是连续的.

红线 : X轴方向;

绿线: Y轴方向;

蓝线: Z轴方向;































### Android 坐标系OpenGL 坐标系的区别 #### 区别一:坐标轴方向不同 在 Android 的视图系统中,坐标系的原点位于屏幕的左上角 (0, 0),x 轴正方向是从左向右,y 轴正方向则是从上向下。而在 OpenGL 中,世界坐标系是以屏幕中心为原点 (0, 0, 0),且是始终不变的。具体来说: - **X轴**:两者相同,都是从左至右。 - **Y轴**:Android 是从上往下,而 OpenGL 则是从下往上[^4]。 此外,在 OpenGL 中还存在 z 轴用于表示深度信息,其正方向指向观察者(即从屏幕内部向外)。 #### 区别二:单位差异 对于 Android 来说,默认情况下使用的通常是像素作为基本度量单位;然而,在 OpenGL ES 环境下,通常采用标准化设备坐标(NDC),其中整个可见区域被映射到了 [-1, 1] 这样一个范围内。 ### 转换方法 为了实现两个坐标系统的相互转换,主要涉及到以下几个方面的工作: #### 平移变换 由于两者的 y 轴方向相反以及原点位置的不同,因此需要通过平移操作来进行调整。假设当前有宽度 w 和高度 h 的 Android 视窗,则可以通过如下方式完成转换: ```java // 将 Android 屏幕坐标转成 NDC (-1 to 1 range) float ndcX = ((androidX / screenWidth) * 2.0f) - 1; float ndcY = (((screenHeight - androidY) / screenHeight) * 2.0f) - 1; // 反之亦然 int screenX = Math.round(((ndcX + 1) / 2.0f) * screenWidth); int screenY = screenHeight - Math.round((((ndcY + 1) / 2.0f)) * screenHeight); ``` #### 正交投影设置 当使用 `Matrix` 类中的 `orthoM()` 函数创建一个正交投影矩阵时,可以根据实际需求指定近裁剪面、远裁剪面以及其他参数来适配不同的显示比例和尺寸[^3]。这有助于确保绘制的内容能够在各种分辨率屏幕上正确呈现而不失真。 #### 使用 GLSurfaceView 实现自动处理 如果开发者利用了 Android 提供的 `GLSurfaceView` 组件及其渲染器接口 (`Renderer`) ,那么很多关于视角变换的任务都可以交给框架本身去完成。只需要关注如何构建合适的场景几何体并应用相应的材质属性即可。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值