十一、OpenGL的坐标系统

news2025/1/18 8:44:39

第一部分概念

OpenGL 坐标系统:OpenGL 坐标系中每个顶点的 x,y,z 坐标都应该在 -1.0 到 1.0 之间,超出这个坐标范围的顶点都将不可见。将一个物体(图像)渲染到屏幕上,通常经过将物体坐标转换为标准化设备坐标,然后再将标准化设备坐标转化为屏幕坐标的过程。

**物体显示:**该过程通常涉及多个坐标系统的变换,将所有顶点转换为片段之前,顶点需要处于不同的坐标系统进行计算,对我们来说比较重要的有 5 个坐标系统:

局部空间:是指对象所在的空间,原点由自己定义,模型的所有顶点对于你的对象来说都是局部的。

世界空间:它可以实现物体的平移,缩放,旋转等操作,就是将他们放在我们指定位置。这些变换是通过模型矩阵(Model Matrix)实现的。在 C/C++ 中可以利用 GLM 构建模型矩阵(直接百度下载开源库,只用把头文件文件夹放入到工程中,进行引用就可以使用了)

glm::mat4 Model = glm::mat4(1.0f); //单位矩阵
Model = glm::scale(Model, glm::vec3(2.0f, 2.0f, 2.0f)); //缩放
Model = glm::rotate(Model, MATH_PI/2, glm::vec3(1.0f, 0.0f, 0.0f)); //沿 x 轴旋转 90 度
Model = glm::translate(Model, glm::vec3(0.0f, 1.0f, 0.0f)); //沿 y 轴正方向平移一个单位

观察空间:称为OpenGL相机空间,就是从摄像机的角度观察得到的空间,他将对象的世界坐标转换为观察者视野前面的坐标,通常可以由一系列的平移和旋转来实现这种方式将对象移到摄像机面前。

// View matrix
glm::mat4 View = glm::lookAt(
			glm::vec3(0, 0, 3), // Camera is at (0,0,1), in World Space 相机位置
			glm::vec3(0, 0, 0), // and looks at the origin 观察点坐标
			glm::vec3(0, 1, 0)  // Head is up (set to 0,-1,0 to look upside-down) 相机 up 方向,即相机头部朝向
);

裁剪空间:用来裁剪观察对象的空间,在顶点着色器运行的最后,OpenGL期望所有的坐标都能落在一个给定的范围内,则这个范围外的点需要被裁剪掉。

  • 投影矩阵**:用来将顶点坐标从观察空间转换到裁剪空间,投影矩阵分为正交投影,透视投影

在这里插入图片描述

前两个参数指定了平截头体的左右坐标,
第三和第四参数指定了平截头体的底部和上部。通过这四个参数我们定义了近平面和远平面的大小,
然后第五和第六个参数则定义了近平面和远平面的距离。这个指定的投影矩阵将处于这些 x,y,z 范围之间的坐标转换到标准化设备坐标系中。
glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f); //ratio 一般表示视口的宽高比,width/height

在这里插入图片描述

第一个参数定义了 fov 的值,它表示的是视野(Field of View),并且设置了观察空间的大小。对于一个真实的观察效果,它的值经常设置为 45.0,但想要看到更多结果你可以设置一个更大的值。
第二个参数设置了宽高比,由视口的高除以宽。
第三和第四个参数设置了平截头体的近和远平面。我们经常设置近距离为 0.1 而远距离设为 100.0 。所有在近平面和远平面的顶点且处于平截头体内的顶点都会被渲染。
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f); //ratio 一般表示视口的宽高比,width/height, 

整个坐标系统的变换矩阵可以用一个矩阵表MVPMatrix = Projection * View * Model

第二部分实践

工程包名

com.example.basiclighting8code

实现 OpenGL 3D 效果最简单的方式是在顶点着色器中将顶点坐标与 MVP 变换矩阵相乘:

java代码和环境部分与之前学习到的opengl显示图片的工程代码是一致的,但是可以引入通过触屏来改变图片位置的参数,从而更形象。C++部分只有顶点的shader不一样,还有在绘画的时候需要对顶点着色器里面的uniform mat4 u_MVPMatrix;//变化矩阵进行设置。

1)C++部分

顶点着色器

#version 300 es
layout (location = 0) in vec4 a_position;
layout (location = 1) in vec2 a_texCoord;
uniform mat4 u_MVPMatrix;//变化矩阵
out vec2 v_texCoord;

void main()
{
    gl_Position = u_MVPMatrix * a_position;
    v_texCoord = a_texCoord;
}

片段着色器

#version 300 es
precision mediump float;
layout(location = 0) out vec4 outColor;
in vec2 v_texCoord;
uniform sampler2D s_TextureMap;
void main()
{
    outColor = texture(s_TextureMap, v_texCoord);
}

NativeGL.cpp

//
// Created by CreatWall_zhouwen on 2023/4/11.
//

#include "jni.h"
#include <android/log.h>
#include <GLES2/gl2.h>
#include "Util.h"
#include "CoordSystem.h"
#include "ReadFileUtil.h"
#define NATIVE_RENDER_CLASS_NAME "com/example/basiclighting8code/NativeImpl"
#define TAG "GLTRIANGLE"
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_sevencoordsystem_NativeImpl
 * Method:    NativeImpl_InitScene
 * Signature: (Ljava/lang/Object;)V
 */
JNIEXPORT void JNICALL NativeImpl_InitScene(JNIEnv *env, jobject instance, jobject msg)
{
   g_mrg = AAssetManager_fromJava(env, msg);
}

/*
 * Class:     com_example_sevencoordsystem_NativeImpl
 * Method:    init
 * Signature: ()V
 */
JNIEXPORT void JNICALL NativeImpl_init(JNIEnv *env, jobject instance)
{
    CoordSystem::GetInstance();
   int width = 0, height = 0;
   unsigned char *img = ReadBMP("awesomeface.bmp", width, height);
    CoordSystem::GetInstance()->getTexturedata(img, width, height);
    CoordSystem::GetInstance()->CreateProgram(
         reinterpret_cast<const char *>(LoadFileContent("vertex.vs")),
         reinterpret_cast<const char *>(LoadFileContent("fShader.fs")));
}

/*
 * Class:     com_example_twodrawtexture_NativeImpl
 * Method:    OnSurfaceChanged
 * Signature: (II)V
 */
JNIEXPORT void JNICALL NativeImpl_OnSurfaceChanged(JNIEnv *env, jobject instance, jint width, jint height)
{
    glViewport(0, 0, width, height);
   CoordSystem::GetInstance()->OnSurfaceChanged(width, height);
}

/*
 * Class:     com_example_twodrawtexture_NativeImpl
 * Method:    OnSurfaceChanged
 * Signature: (FFFF)V
 */
JNIEXPORT void JNICALL native_UpdateTransformMatrix(JNIEnv *env, jobject instance, jfloat rotateX, jfloat rotateY, jfloat scaleX, jfloat scaleY)
{
   //通过这个方法来改变图片的坐标
   CoordSystem::GetInstance()->UpdateTransformMatrix(rotateX, rotateY, scaleX, scaleY);
}

/*
 * Class:     com_example_twodrawtexture_NativeImpl
 * Method:    draw
 * Signature: ()V
 */
JNIEXPORT void JNICALL NativeImpl_draw(JNIEnv *env, jobject instance)
{
    CoordSystem::GetInstance()->Draw();
}


#ifdef __cplusplus
}
#endif

static JNINativeMethod g_RenderMethods[] = {
      {"NativeImpl_InitScene",             "(Ljava/lang/Object;)V",       (void *)(NativeImpl_InitScene)},
      {"NativeImpl_init",             "()V",       (void *)(NativeImpl_init)},
      {"NativeImpl_OnSurfaceChanged",   "(II)V",     (void *)(NativeImpl_OnSurfaceChanged)},
      {"NativeImpl_draw",        "()V",       (void *)(NativeImpl_draw)},
      {"native_UpdateTransformMatrix",        "(FFFF)V",       (void *)(native_UpdateTransformMatrix)},
};

static int RegisterNativeMethods(JNIEnv *env, const char *className, JNINativeMethod *methods, int methodNum)
{
   LOGD("RegisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("RegisterNativeMethods fail. clazz == NULL");
      return JNI_FALSE;
   }
   if (env->RegisterNatives(clazz, methods, methodNum) < 0)
   {
      LOGD("RegisterNativeMethods fail");
      return JNI_FALSE;
   }
   return JNI_TRUE;
}

static void UnregisterNativeMethods(JNIEnv *env, const char *className)
{
   LOGD("UnregisterNativeMethods");
   jclass clazz = env->FindClass(className);
   if (clazz == NULL)
   {
      LOGD("UnregisterNativeMethods fail. clazz == NULL");
      return;
   }
   if (env != NULL)
   {
      env->UnregisterNatives(clazz);
   }
}

// call this func when loading lib
extern "C" jint JNI_OnLoad(JavaVM *jvm, void *p)
{
   LOGD("===== JNI_OnLoad =====");
   jint jniRet = JNI_ERR;
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return jniRet;
   }

   jint regRet = RegisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME, g_RenderMethods,
                              sizeof(g_RenderMethods) /
                              sizeof(g_RenderMethods[0]));
   if (regRet != JNI_TRUE)
   {
      return JNI_ERR;
   }

   return JNI_VERSION_1_6;
}

extern "C" void JNI_OnUnload(JavaVM *jvm, void *p)
{
   JNIEnv *env = NULL;
   if (jvm->GetEnv((void **) (&env), JNI_VERSION_1_6) != JNI_OK)
   {
      return;
   }

   UnregisterNativeMethods(env, NATIVE_RENDER_CLASS_NAME);
}

CoordSystem.h

//
// Created by CreatWall_zhouwen on 2023/4/13.
//

#ifndef TWODRAWTEXTURE_TEXTUREDEMO_H
#define TWODRAWTEXTURE_TEXTUREDEMO_H


#include <GLES3/gl3.h>
#include <detail/type_mat.hpp>
#include <detail/type_mat4x4.hpp>
#define MATH_PI 3.1415926535897932384626433832802
class CoordSystem {
public:
    CoordSystem(){
        program = 0;
        vertexShaderHandle = 0;
        fragShaderHandle = 0;
    };
    ~CoordSystem(){};
    void CreateProgram(const char *ver, const char *frag);
    void Draw();
    void getTexturedata(unsigned char *data, int width, int height);
    static CoordSystem* GetInstance();
    static void DestroyInstance();
    void OnSurfaceChanged(int width, int height);
    void UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);
    void UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio);
private:
    GLuint program;
    GLuint vertexShaderHandle;
    GLuint fragShaderHandle;
    GLuint m_VaoId;//存放顶点数据
    GLuint m_VboIds[3];//0表示顶点坐标缓冲区,1表示离屏渲染纹理坐标缓冲区,2表示纹理索引坐标缓冲区
    unsigned int m_TextureId;
    unsigned char *texturedata;
    int texturewidth, textureheight;

    int srceenWidth, srceenHeight;//屏幕宽高
    GLint m_SamplerLoc;
    GLint m_MVPMatLoc;
    glm::mat4 m_MVPMatrix;
    int m_AngleX;
    int m_AngleY;
    float m_ScaleX;
    float m_ScaleY;
};



#endif //TWODRAWTEXTURE_TEXTUREDEMO_H

CoordSystem.cpp

//
// Created by CreatWall_zhouwen on 2023/4/13.
//

#include "CoordSystem.h"
#include "Util.h"
#include "GLUtil.h"
#include <gtc/matrix_transform.hpp>
CoordSystem* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"


float vertices[] = {
        -1.0f, -1.0f, 0.0f,
        1.0f, -1.0f, 0.0f,
        -1.0f,  1.0f, 0.0f,
        1.0f,  1.0f, 0.0f,
};
//纹理坐标
GLfloat vTexCoors[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
};
//索引数组
unsigned int indices[] = {
        0, 1, 2, 1, 3, 2
};

void CoordSystem::CreateProgram(const char *ver, const char *frag)
{
    LOGD("CreateProgram Enter");
    // 编译链接用于离屏渲染的着色器程序
    program = CreateGLProgram(ver, frag, vertexShaderHandle, fragShaderHandle);
    if (program == GL_NONE)
    {
        LOGD("FBOSample::Init m_ProgramObj == GL_NONE");
        return;
    }
    LOGD("CreateGLProgram Success");
    m_SamplerLoc = glGetUniformLocation(program, "s_TextureMap");
    m_MVPMatLoc = glGetUniformLocation(program, "u_MVPMatrix");
    /* -----顶点相关------ */
    glGenBuffers(4, m_VboIds);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vTexCoors), vTexCoors, GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);//最后一个为纹理的索引缓冲数据
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    LOGD("glGenBuffers Success");

    glGenVertexArrays(1, &m_VaoId);
    //初始化离屏渲染的VAO
    glBindVertexArray(m_VaoId);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void *)0);
    glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_VboIds[2]);
    glBindVertexArray(GL_NONE);
    LOGD("m_VaoIds[1] Success");

    /* -----创建加载纹理------ */
    //创建纹理
    glGenTextures(1, &m_TextureId);
    //将纹理绑定到GL_TEXTURE_2D
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    //为当前绑定的纹理对象设定纹理环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//重复纹理的填充方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    //为当前绑定的纹理对象设定纹理过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//缩小时线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//放到就是线性

    //绑定纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, texturewidth, textureheight, 0, GL_RGB, GL_UNSIGNED_BYTE, texturedata);
    LOGD("CreateProgram %s", texturedata);
    glGenerateMipmap(GL_TEXTURE_2D);//为当前绑定的纹理自动生成所有需要的多级渐远纹理
    LOGD("CreateProgram END");
}
void CoordSystem::Draw()
{
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glClearColor(0.2f, 0.9f, 0.3f, 1.0f);
    UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, (float)srceenWidth / srceenHeight);
    glUseProgram (program);

    glBindVertexArray(m_VaoId);

    glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);

    // Bind the RGBA map
    //glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_TextureId);
    glUniform1i(m_SamplerLoc, 0);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (const void *)0);
    glBindVertexArray(0);

}
CoordSystem* CoordSystem::GetInstance()
{
    if (m_pContext == nullptr)
    {
        m_pContext = new CoordSystem();
    }
    return m_pContext;
}
void CoordSystem::DestroyInstance()
{
    if (m_pContext)
    {
        delete m_pContext;
        m_pContext = nullptr;
    }
}

void CoordSystem::getTexturedata(unsigned char *data, int width, int height)
{
    texturedata = data;
    texturewidth = width;
    textureheight = height;
}

void CoordSystem::OnSurfaceChanged(int width, int height) {
    glViewport(0, 0, width, height);
    srceenWidth = width;
    srceenHeight = height;
}
/**
 * @param angleX 绕X轴旋转度数
 * @param angleY 绕Y轴旋转度数
 * @param ratio 宽高比
 * */
void CoordSystem::UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float ratio)
{
    LOGD("CoordSystemSample::UpdateMVPMatrix angleX = %d, angleY = %d, ratio = %f", angleX, angleY, ratio);
    angleX = angleX % 360;
    angleY = angleY % 360;

    //转化为弧度角
    float radiansX = static_cast<float>(MATH_PI / 180.0f * angleX);
    float radiansY = static_cast<float>(MATH_PI / 180.0f * angleY);


    // Projection matrix
    //glm::mat4 Projection = glm::ortho(-1.0f, 1.0f, -1.0f, 1.0f, 0.1f, 100.0f);
    //glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
    glm::mat4 Projection = glm::perspective(45.0f,ratio, 0.1f,100.f);

    // View matrix
    glm::mat4 View = glm::lookAt(
            glm::vec3(0, 0, 4), // Camera is at (0,0,1), in World Space
            glm::vec3(0, 0, 0), // and looks at the origin
            glm::vec3(0, 1, 0)  // Head is up (set to 0,-1,0 to look upside-down)
    );

    // Model matrix
    glm::mat4 Model = glm::mat4(1.0f);
    Model = glm::scale(Model, glm::vec3(m_ScaleX, m_ScaleY, 1.0f));//缩放
    Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
    Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
    Model = glm::translate(Model, glm::vec3(0.0f, 0.0f, 0.0f));

    mvpMatrix = Projection * View * Model;
    for (int i = 0; i < 4; ++i) {
        LOGD("CoordSystemSample::UpdateMVPMatrix angleX = %f %f %f %f",mvpMatrix[i][0],mvpMatrix[i][1],mvpMatrix[i][2],mvpMatrix[i][3]);
    }

}

void CoordSystem::UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY) {
    m_AngleX = static_cast<int>(rotateX);
    m_AngleY = static_cast<int>(rotateY);
    m_ScaleX = scaleX;
    m_ScaleY = scaleY;
}

2) Java部分

MyGLSurfaceView.java

package com.example.basiclighting8code;

import android.content.Context;
import android.content.res.AssetManager;
import android.opengl.GLSurfaceView;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;

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

public class MyGLSurfaceView extends GLSurfaceView implements ScaleGestureDetector.OnScaleGestureListener{
    private MyRenderer renderer;
    private AssetManager mrg;

    private final float TOUCH_SCALE_FACTOR = 180.0f / 320;
    private float mPreviousY;
    private float mPreviousX;
    private int mXAngle;
    private int mYAngle;
    private int mRatioWidth = 0;
    private int mRatioHeight = 0;

    private ScaleGestureDetector mScaleGestureDetector;
    private float mPreScale = 1.0f;
    private float mCurScale = 1.0f;
    private long mLastMultiTouchTime;
    public MyGLSurfaceView(Context context) {
        super(context);
        mrg = getResources().getAssets();
        setEGLContextClientVersion(3);
        setEGLConfigChooser(8, 8, 8, 8, 16, 8);
        renderer = new MyRenderer();
        setRenderer(renderer);
        setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
        mScaleGestureDetector = new ScaleGestureDetector(context, this);
    }


    //屏幕触摸事件
    public boolean onTouchEvent(MotionEvent e) {
        if (e.getPointerCount() == 1) {
            consumeTouchEvent(e);
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - mLastMultiTouchTime > 200) {
                float y = e.getY();
                float x = e.getX();
                switch (e.getAction()) {
                    case MotionEvent.ACTION_MOVE:
                        float dy = y - mPreviousY;
                        float dx = x - mPreviousX;
                        mYAngle += dx * TOUCH_SCALE_FACTOR;
                        mXAngle += dy * TOUCH_SCALE_FACTOR;
                }
                mPreviousY = y;
                mPreviousX = x;
                //更新xy的角度
                renderer.updateTransformMatrix(mXAngle, mYAngle, mCurScale, mCurScale);
                requestRender();
            }else {
                mScaleGestureDetector.onTouchEvent(e);
            }
        }
        return true;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (0 == mRatioWidth || 0 == mRatioHeight) {
            setMeasuredDimension(width, height);
        } else {
            if (width < height * mRatioWidth / mRatioHeight) {
                setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
            } else {
                setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
            }
        }
    }

    public void setAspectRatio(int width, int height) {
        if (width < 0 || height < 0) {
            throw new IllegalArgumentException("Size cannot be negative.");
        }
        mRatioWidth = width;
        mRatioHeight = height;
        requestLayout();
    }

    public void consumeTouchEvent(MotionEvent e) {
        dealClickEvent(e);
        float touchX = -1, touchY = -1;
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                touchX = e.getX();
                touchY = e.getY();
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                touchX = -1;
                touchY = -1;
                break;
            default:
                break;
        }

        //滑动、触摸
        /*switch (mGLRender.getSampleType()) {
            case SAMPLE_TYPE_KEY_SCRATCH_CARD:
                mGLRender.setTouchLoc(touchX, touchY);
                requestRender();
                break;
            default:
                break;
        }*/

        //点击
        switch (e.getAction()) {
            case MotionEvent.ACTION_MOVE:
                //touchX = e.getX();
                //touchY = e.getY();
                break;
            case MotionEvent.ACTION_UP:
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            default:
                break;
        }

    }

    public void dealClickEvent(MotionEvent e) {
        float touchX = -1, touchY = -1;
        switch (e.getAction()) {
            case MotionEvent.ACTION_UP:
                touchX = e.getX();
                touchY = e.getY();
                break;
            default:
                break;
        }
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        float preSpan = detector.getPreviousSpan();
        float curSpan = detector.getCurrentSpan();
        if (curSpan < preSpan) {
            mCurScale = mPreScale - (preSpan - curSpan) / 200;
        } else {
            mCurScale = mPreScale + (curSpan - preSpan) / 200;
        }
        mCurScale = Math.max(0.05f, Math.min(mCurScale, 80.0f));
        renderer.updateTransformMatrix(mXAngle, mYAngle, mCurScale, mCurScale);
        requestRender();
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
        return false;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
        mPreScale = mCurScale;
        mLastMultiTouchTime = System.currentTimeMillis();
    }

    private class MyRenderer implements Renderer {
        @Override
        public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {
            NativeImpl.NativeImpl_InitScene(mrg);
            NativeImpl.NativeImpl_init();
        }

        @Override
        public void onSurfaceChanged(GL10 gl10, int width, int height) {
            NativeImpl.NativeImpl_OnSurfaceChanged(width, height);
        }

        @Override
        public void onDrawFrame(GL10 gl10) {
            NativeImpl.NativeImpl_draw();
        }

        public void updateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY)
        {
            NativeImpl.native_UpdateTransformMatrix(rotateX, rotateY, scaleX, scaleY);
        }
    }
}

#

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/619663.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

30天从入门到精通TensorFlow1.x 第七天,TensorFlow1.x 模型save()和restore()

文章目录 一、接前一天二、TensorFlow中模型的保存和加载方式三、模型的保存和加载Save()类1. 创建方法以及文件组成2. Saver类的重要参数参数&#xff1a;重要函数参数&#xff1a; 3. Saver类的主要使用函数 四、keras的模型保存和加载1. Keras比较简单&#xff1a;一般有三种…

PLSQL - Months_Between的理解和使用

Months_Between是一个内置的日期操纵函数&#xff0c;用于计算两个日期相隔的月份数。Oracle文档的介绍如下&#xff1a; MONTHS_BETWEEN returns number of months between dates date1 and date2. The month and the last day of the month are defined by the parameter NL…

jenkins主从节点安装及pipeline构建

一、背景 通过Jenkins主节点配置的pipeline下发给从节点执行&#xff0c;从而兼容容器化执行 二、安装主节点 docker-compose.yml jenkins:user: rootrestart: alwaysimage: jenkinsci/blueoceancontainer_name: jenkins# network_mode: hostports:- "8081:8080"-…

BOS EDI Excel 方案简介

BOS EDI & Excel 方案简介 本文将继续分享BOS示例工作流&#xff1a;使用Excel端口和Email端口生成一系列文件&#xff0c;完成与BOS的EDI通信。 下载工作流 下载示例文件 BOS EDI到Excel示例流具有预配置的端口&#xff0c;用于从BOS的EDI集成规范转换以下交易集&…

2022年国赛高教杯数学建模D题气象报文信息卫星通信传输解题全过程文档及程序

2022年国赛高教杯数学建模 D题 气象报文信息卫星通信传输 原题再现 在某些紧急救援任务中&#xff0c;需要进行物资空投。在地面通信系统瘫痪的情形下&#xff0c;为了更好地获得准确完整的地面气象观测信息&#xff0c;通常对任务区域的重要目标点采用派遣气象分队的方式来获…

如果让你来设计CPU之内存篇

哈喽&#xff0c;我是子牙&#xff0c;一个很卷的硬核男人。深入研究Windows内核、Linux内核、Hotspot源码…聚焦做那些大家想学没地方学的课程&#xff1a;手写操作系统、手写虚拟机、手写模拟器、手写编程语言… 目前已经做了两个成熟的课程&#xff1a;手写JVM、手写OS&…

PPP认证协议详解

PPP认证协议详解 1. 引言 PPP&#xff08;Point-to-Point Protocol&#xff09;认证协议在计算机网络中扮演着重要的角色。它是一种用于建立和认证网络连接的协议&#xff0c;广泛应用于各种网络环境&#xff0c;包括互联网接入、虚拟专用网络&#xff08;VPN&#xff09;和远…

【头歌】试的学习

1.基本路径测试 2.画出程序控制流图 3.计算流图的环形复杂度 4.确定线性独立路径的基本集合 5.设计测试用例 基本路径测试 除了逻辑覆盖&#xff0c;还有一种常用的白盒测试的测试方法&#xff1a;基本路径测试。基本路径测试是 Tom McCabe提出的一种白盒测试技术。使用这种技…

c++学习——继承

继承 **继承****继承的案例****继承的三种方式方式&#xff1a;****继承中的对象类型****继承中的构造和析构顺序****继承中同名成员的处理****同名静态成员处理****多继承语法****菱形继承** 继承 普通的输出 #define _CRT_SECURE_NO_WARNINGS #include <iostream> us…

8. 让java性能提升的JIT深度解剖

JVM性能调优 1. C1、C2与Graal编译器1.1 C1编译器1.2 C2编译器1.3 分层编译 2. 热点代码3. 热点探测4. 方法调用计数器5. 回边计数器6. 编译优化技术6.1 方法内联 7. 锁消除8. 栈上分配9. 逃逸分析技术10. 标量替换 本文是按照自己的理解进行笔记总结&#xff0c;如有不正确的地…

【LeetCode热题100】打卡第14天:下一个排列最长有效括号

文章目录 【LeetCode热题100】打卡第14天&#xff1a;下一个排列&最长有效括号下一个排列⛅前言&#x1f512;题目&#x1f511;题解 最长有效括号&#x1f512;题目&#x1f511;题解 【LeetCode热题100】打卡第14天&#xff1a;下一个排列&最长有效括号 下一个排列 …

如何入门挖掘SRC?

挖洞其实算是web渗透中第一个明确的关卡 越过这个坎&#xff0c;从此天高任鸟飞&#xff0c;海阔凭鱼跃。越不过&#xff0c;就永远越不过。 先说平台&#xff1a; 漏洞响应平台&#xff1a;实战渗透测试&#xff0c;同时能获得一些外快。 补天漏洞响应平台&#xff1a;http…

Netty核心技术五--Netty高性能架构设计

1. 线程模型基本介绍 不同的线程模式&#xff0c;对程序的性能有很大影响&#xff0c;为了搞清Netty 线程模式&#xff0c;我们来系统的讲解下 各个线程模式&#xff0c; 最后看看Netty 线程模型有什么优越性.目前存在的线程模型有: 传统阻塞 I/O 服务模型Reactor 模式 根据 R…

郭光灿团队实现低温集成量子纠缠光源

中国科大郭光灿院士团队在集成化量子光源制备研究中取得重要进展。该团队任希锋研究组基于低温集成自发四波混频过程&#xff0c;展示了低温条件下集成量子纠缠光源的制备&#xff0c;相关成果于6月2日发表在光学知名学术期刊Optica上。 “利用低温综合四波混合技术产生纠缠现象…

Mapbox表达式详细解读

初学mapbox 的小伙伴们一定会被表达式给弄的晕头转向的。明明条件判断或者回调函数能解决的问题。mapbox里非得让你用表达式。这确实比较ex。 不过我们既然遇到了,也不要怕,这篇文章我就带着大家一点一点的搞明白这个所谓的表达式。 首先从宏观上讲,要知道为什么使用表达式…

【面试高频】cookie、session、token?看完再也不担心被问了

在以往的面试记录里&#xff0c;我又看到了一个多次被问到的知识点&#xff0c;那就是 cookie、session、token 的区别有哪些&#xff1f;如果现在来问你&#xff0c;不知道你能否说清楚呢&#xff1f; 今天不仅仅是整理出这三者的区别&#xff0c;更重要的是能够真正去理解这三…

Python | print写入日志

Python | print写入日志 有时我们需要将屏幕上打印的消息保存到一个文件中&#xff0c;如果每条信息都通过调用写入函数来实现&#xff0c;就太麻烦了 这里自己定义1个日志类&#xff0c;然后将 sys.stdout 设置为该类即可&#xff0c;非常方便 sys.stdout Logger(fileName …

卡尔曼滤波与组合导航原理(八)遗忘滤波

函数模型 { X k Φ k l k − 1 X k − 1 Γ k − 1 W k − 1 Z k H k X k V k \left\{\begin{array}{l} \boldsymbol{X}_{k}\boldsymbol{\Phi}_{k l k-1} \boldsymbol{X}_{k-1}\boldsymbol{\Gamma}_{k-1} \boldsymbol{W}_{k-1} \\ \boldsymbol{Z}_{k}\boldsymbol{H}_{k} \…

C语言:使用 普通方法 和 二分查找算法(折半查找算法) 在一个有序数组中查找具体的某个数字n

题目&#xff1a; 从键盘输入数字n&#xff0c;在一个 有序数组 中查找具体的某个数字n。 思路一&#xff1a;普通方法 &#xff08;逻辑简单&#xff0c;在无序数组中也可以使用&#xff0c;但效率较低&#xff0c;需要逐个查找&#xff09; 总体思路&#xff1a; &#xff…

日常培训管理-参训名单/BootstrapTable获取表数据 / js 删除两个数组中id相同的对象/

---2022.11.9 1、 现在有一个功能是从下面待选名单中选中&#xff0c;再点击这个添加按钮&#xff0c;就会将这些人添加到上面这个参训名单&#xff0c;然后再给其中每个人手动打分。分打完 BootstrapTable中有两组数据&#xff0c;在下面待选名单数据条目前面中打钩选中&am…