1 前言

正方形图片贴到圆形上 中将正方形图片上的纹理映射到圆形模型上,同理,也可以将圆形上的纹理映射到凸镜的球形曲面上。如下图,最左边的竖条是原图片的截面(纹理坐标),最右边的竖条是变换后的顶点模型截面(顶点坐标)。

原图如下:

读者如果对 OpenGL ES 不太熟悉,请回顾以下内容:

  • 绘制三角形​​​​​​
  • ​​​​​​绘制立方体
  • MVP矩阵变换
  • 纹理贴图
  • 正方形图片贴到圆形上

Unity3D Shader 版本实现见→半球卷屏特效。

本文完整代码资源见→【OpenGL ES】凸镜贴图

项目目录如下:

2 案例

MainActivity.java

package com.zhyan8.convexMirror.activity;import android.opengl.GLSurfaceView;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.zhyan8.convexMirror.opengl.MyGLSurfaceView;
import com.zhyan8.convexMirror.opengl.MyRender;public class MainActivity extends AppCompatActivity {private GLSurfaceView mGlSurfaceView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mGlSurfaceView = new MyGLSurfaceView(this);setContentView(mGlSurfaceView);mGlSurfaceView.setRenderer(new MyRender(getResources()));}@Overrideprotected void onResume() {super.onResume();mGlSurfaceView.onResume();}@Overrideprotected void onPause() {super.onPause();mGlSurfaceView.onPause();}
}

MyGLSurfaceView.java

package com.zhyan8.convexMirror.opengl;import android.content.Context;
import android.opengl.GLSurfaceView;
import android.util.AttributeSet;public class MyGLSurfaceView extends GLSurfaceView {public MyGLSurfaceView(Context context) {super(context);setEGLContextClientVersion(3);}public MyGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);setEGLContextClientVersion(3);}
}

MyRender.java

package com.zhyan8.convexMirror.opengl;import android.content.res.Resources;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;
import com.zhyan8.convexMirror.model.Model;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;public class MyRender implements GLSurfaceView.Renderer {private Model mModel;public MyRender(Resources resources) {mModel = new Model(resources);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig eglConfig) {//设置背景颜色GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//启动深度测试gl.glEnable(GLES30.GL_DEPTH_TEST);mModel.onModelCreate();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {//设置视图窗口GLES30.glViewport(0, 0, width, height);mModel.onModelChange(width, height);}@Overridepublic void onDrawFrame(GL10 gl) {//将颜色缓冲区设置为预设的颜色GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT | GLES30.GL_DEPTH_BUFFER_BIT);//启用顶点的数组句柄GLES30.glEnableVertexAttribArray(0);GLES30.glEnableVertexAttribArray(1);//绘制模型mModel.onModelDraw();//禁止顶点数组句柄GLES30.glDisableVertexAttribArray(0);GLES30.glDisableVertexAttribArray(1);}
}

Model.java

package com.zhyan8.convexMirror.model;import android.content.res.Resources;
import android.opengl.GLES30;
import com.zhyan8.convexMirror.R;
import com.zhyan8.convexMirror.utils.ArraysUtils;
import com.zhyan8.convexMirror.utils.ShaderUtils;
import com.zhyan8.convexMirror.utils.TextureUtils;
import java.nio.FloatBuffer;public class Model {private static final int RING_NUM = 30; // 环数private static final int RAW_NUM = 50; // 射线数private static final double CONE_SECTION_FAN_ANGLE = 170.0 / 180.0 * Math.PI; // 圆锥凸镜截面扇形弧度(180度为半球)private static final double RING_WIDTH = 1.0 / RING_NUM; // 环宽度private static final double RAW_GAP_ANGLE = 2 * Math.PI / RAW_NUM; // 两条射线间最小夹角private static final int TEXTURE_DIMENSION = 2; // 纹理坐标维度private static final int VERTEX_DIMENSION = 3; // 顶点坐标维度private static final double EPSILON = 0.00000000001; // epsilon,比较小的浮点常量private Resources mResources;private MyTransform mTransform;private float[][] mTextures;private float[][] mVertices;private FloatBuffer[] mTexturesBuffers;private FloatBuffer[] mVerticesBuffers;private int mTextureId;private int mProgramId;private int mPointNumPerRing;public Model(Resources resources) {mResources = resources;mPointNumPerRing = (RAW_NUM + 1) * 2;mTextures = new float[RING_NUM][mPointNumPerRing * TEXTURE_DIMENSION];mVertices = new float[RING_NUM][mPointNumPerRing * VERTEX_DIMENSION];mTexturesBuffers = new FloatBuffer[RING_NUM];mVerticesBuffers = new FloatBuffer[RING_NUM];mTransform = new MyTransform();}// 模型创建public void onModelCreate() {for (int i = 0; i < RING_NUM; i++) {getRingTexture(i);getRingVertex(i);mTexturesBuffers[i] = ArraysUtils.getFloatBuffer(mTextures[i]);mVerticesBuffers[i] = ArraysUtils.getFloatBuffer(mVertices[i]);}mProgramId = ShaderUtils.createProgram(mResources, R.raw.vertex_shader, R.raw.fragment_shader);mTextureId = TextureUtils.loadTexture(mResources, R.raw.zzz);mTransform.onTransformCreate(mProgramId);}// 模型参数变化public void onModelChange(int width, int height) {mTransform.onTransformChange(width, height);}// 模型绘制public void onModelDraw() {GLES30.glUseProgram(mProgramId);mTransform.onTransformExecute();for (int i = 0; i < RING_NUM; i++) { // 一环一环绘制纹理//准备顶点坐标和纹理坐标GLES30.glVertexAttribPointer(0, VERTEX_DIMENSION, GLES30.GL_FLOAT, false, 0, mVerticesBuffers[i]);GLES30.glVertexAttribPointer(1, TEXTURE_DIMENSION, GLES30.GL_FLOAT, false, 0, mTexturesBuffers[i]);//激活纹理GLES30.glActiveTexture(GLES30.GL_TEXTURE);//绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureId);//绘制贴图GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, mPointNumPerRing);
//            GLES30.glDrawArrays(GLES30.GL_LINE_STRIP, 0, mPointNumPerRing);}}// 计算纹理坐标private void getRingTexture(int ring) {int rayNum = RAW_NUM + 1;double theta = 0;double radius1 = ring * RING_WIDTH / 2;double radius2 = (ring + 1) * RING_WIDTH / 2;int index = 0;for (int i = 0; i < rayNum; i++) {mTextures[ring][index++] = (float) (0.5 + radius1 * Math.cos(theta));mTextures[ring][index++] = (float) (0.5 - radius1 * Math.sin(theta));mTextures[ring][index++] = (float) (0.5 + radius2 * Math.cos(theta));mTextures[ring][index++] = (float) (0.5 - radius2 * Math.sin(theta));theta += RAW_GAP_ANGLE;}}// 计算顶点坐标private void getRingVertex(int ring) {int index = 0;for (int i = 0; i < mTextures[ring].length; i += TEXTURE_DIMENSION) {float[] pos = getPosInWorldAxis(mTextures[ring][i], mTextures[ring][i + 1]);textureVertexMapping(pos);mVertices[ring][index++] = pos[0];mVertices[ring][index++] = pos[1];mVertices[ring][index++] = pos[2];}}// 纹理坐标映射到顶点坐标private void textureVertexMapping(float[] pos) {double norm = Math.sqrt(pos[0] * pos[0] + pos[1] * pos[1]);double alpha = CONE_SECTION_FAN_ANGLE / 2;double beta = norm * alpha;if (norm > EPSILON) {double factor = Math.sin(beta) / Math.sin(alpha) / norm;pos[0] = (float) (pos[0] * factor);pos[1] = (float) (pos[1] * factor);}pos[2] = (float) ((Math.cos(beta) - Math.cos(alpha)) / Math.sin(alpha));}//纹理坐标转换为世界坐标private float[] getPosInWorldAxis(float x, float y) {float [] pos = new float[VERTEX_DIMENSION];pos[0] = x * 2 - 1;pos[1] = 1 - y * 2;pos[2] = 1f;return pos;}
}

MyTransform.java

package com.zhyan8.convexMirror.model;import android.opengl.GLES30;
import android.opengl.Matrix;public class MyTransform {private static final float SCALE_RATE = 1.0f; // 模型缩放系数private int mProgramId;private float mRatio;private int mRotateAgree = 0;private int mMvpMatrixHandle;private float[] mModelMatrix;private float[] mViewMatrix;private float[] mProjectionMatrix;private float[] mMvpMatrix;// 变换创建public void onTransformCreate(int programId) {mProgramId = programId;mMvpMatrixHandle = GLES30.glGetUniformLocation(mProgramId, "mvpMatrix");mViewMatrix = getIdentityMatrix(16, 0);mMvpMatrix = getIdentityMatrix(16, 0);Matrix.setLookAtM(mViewMatrix, 0, 0, 0, 6, 0, 0, 0, 0, 1, 0);}// 变换参数变换public void onTransformChange(int width, int height) {mRatio = 1.0f * width / height;mProjectionMatrix = getIdentityMatrix(16, 0);Matrix.frustumM(mProjectionMatrix, 0, -mRatio, mRatio, -1, 1, 3, 20);}// 变换执行public void onTransformExecute() {mModelMatrix = getIdentityMatrix(16, 0);mRotateAgree = (mRotateAgree + 1) % 360;Matrix.rotateM(mModelMatrix, 0, mRotateAgree, -1, -1, 1);Matrix.scaleM(mModelMatrix, 0, SCALE_RATE, SCALE_RATE, SCALE_RATE);//计算MVP变换矩阵: mvpMatrix = projectionMatrix * viewMatrix * modelMatrixfloat[] tempMatrix = new float[16];Matrix.multiplyMM(tempMatrix, 0, mViewMatrix, 0, mModelMatrix, 0);Matrix.multiplyMM(mMvpMatrix, 0, mProjectionMatrix, 0, tempMatrix, 0);GLES30.glUniformMatrix4fv(mMvpMatrixHandle, 1, false, mMvpMatrix, 0);}private float[] getIdentityMatrix(int size, int offset) {float[] matrix = new float[size];Matrix.setIdentityM(matrix, offset);return matrix;}
}

ShaderUtils.java

package com.zhyan8.convexMirror.utils;import android.content.res.Resources;
import android.opengl.GLES30;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;public class ShaderUtils {//创建程序idpublic static int createProgram(Resources resources, int vertexShaderResId, int fragmentShaderResId) {final int vertexShaderId = compileShader(resources, GLES30.GL_VERTEX_SHADER, vertexShaderResId);final int fragmentShaderId = compileShader(resources, GLES30.GL_FRAGMENT_SHADER, fragmentShaderResId);return linkProgram(vertexShaderId, fragmentShaderId);}//通过外部资源编译着色器private static int compileShader(Resources resources, int type, int shaderId){String shaderCode = readShaderFromResource(resources, shaderId);return compileShader(type, shaderCode);}//通过代码片段编译着色器private static int compileShader(int type, String shaderCode){int shader = GLES30.glCreateShader(type);GLES30.glShaderSource(shader, shaderCode);GLES30.glCompileShader(shader);return shader;}//链接到着色器private static int linkProgram(int vertexShaderId, int fragmentShaderId) {final int programId = GLES30.glCreateProgram();//将顶点着色器加入到程序GLES30.glAttachShader(programId, vertexShaderId);//将片元着色器加入到程序GLES30.glAttachShader(programId, fragmentShaderId);//链接着色器程序GLES30.glLinkProgram(programId);return programId;}//从shader文件读出字符串private static String readShaderFromResource(Resources resources, int shaderId) {InputStream is = resources.openRawResource(shaderId);BufferedReader br = new BufferedReader(new InputStreamReader(is));String line;StringBuilder sb = new StringBuilder();try {while ((line = br.readLine()) != null) {sb.append(line);sb.append("\n");}br.close();} catch (Exception e) {e.printStackTrace();}return sb.toString();}
}

TextureUtils.java

package com.zhyan8.convexMirror.utils;import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLES30;
import android.opengl.GLUtils;public class TextureUtils {//加载纹理贴图public static int loadTexture(Resources resources, int resourceId) {BitmapFactory.Options options = new BitmapFactory.Options();options.inScaled = false;Bitmap bitmap = BitmapFactory.decodeResource(resources, resourceId, options);final int[] textureIds = new int[1];// 生成纹理idGLES30.glGenTextures(1, textureIds, 0);// 绑定纹理到OpenGLGLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0]);GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MIN_FILTER, GLES30.GL_LINEAR_MIPMAP_LINEAR);GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D, GLES30.GL_TEXTURE_MAG_FILTER, GLES30.GL_LINEAR);// 加载bitmap到纹理中GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0);// 生成MIP贴图GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D);// 取消绑定纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0);return textureIds[0];}
}

ArraysUtils.java

package com.zhyan8.convexMirror.utils;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;public class ArraysUtils {public static FloatBuffer getFloatBuffer(float[] floatArr) {FloatBuffer fb = ByteBuffer.allocateDirect(floatArr.length * Float.BYTES).order(ByteOrder.nativeOrder()).asFloatBuffer();fb.put(floatArr);fb.position(0);return fb;}
}

vertex_shader.glsl

#version 300 es
layout (location = 0) in vec4 vPosition;
layout (location = 1) in vec2 aTextureCoord;
uniform mat4 mvpMatrix;
out vec2 vTexCoord;
void main() {gl_Position  = mvpMatrix * vPosition;vTexCoord = aTextureCoord;
}

fragment_shader.glsl

#version 300 es
precision mediump float;
uniform sampler2D uTextureUnit;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {fragColor = texture(uTextureUnit,vTexCoord);
}

3 运行效果

        

【OpenGL ES】凸镜贴图相关推荐

  1. opengl es纹理贴图效果实例

    一.先准备好一张用来贴图的照片 二.纹理效果代码: gl.glEnable(GL10.GL_TEXTURE_2D);// 创建纹理gl.glGenTextures(1, textureids, 0); ...

  2. OpenGL ES 3. 天空盒 立方体贴图

    大家好,接下来将为大家介绍OpenGL ES 3. 天空盒 立方体贴图. OpenGL ES 立方体贴图本质上还是纹理映射,是一种 3D 纹理映射.立方体贴图所使的纹理称为立方图纹理,它是由 6 个单 ...

  3. Windows OpenGL ES 图像绿幕抠图

    目录 一.OpenGL ES 图像绿幕抠图 1.原始图片 2.效果演示 二.OpenGL ES 图像绿幕抠图源码下载 三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学 ...

  4. Windows OpenGL ES 图像单色

    目录 一.OpenGL ES 图像单色 1.原始图片 2.效果演示 二.OpenGL ES 图像单色源码下载 三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 ...

  5. 在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping)

    在 OpenGL ES 2.0 上实现视差贴图(Parallax Mapping) 视差贴图 最近一直在研究如何在我的 iPad 2(只支持 OpenGL ES 2.0, 不支持 3.0) 上实现 视 ...

  6. 2.x最终照着教程,成功使用OpenGL ES 绘制纹理贴图,添加了灰度图

    在之前成功绘制变色的几何图形之后,今天利用Openg ES的可编程管线绘制出第一张纹理. 学校时候不知道OpenGL的重要性,怕晦涩的语法.没有跟老师学习OpenGL的环境配置,现在仅仅能利用coco ...

  7. OpenGL ES教程VI之纹理贴图(原文对照)

    注:又是一篇,是否有人能解释得清楚,2是重复两次,那么是否N就是重复N次呢?接近1.0的坐标值整数部分加上几就是重复几次吗?这个好像之前验证过不一定的. 转自:http://melord.iteye. ...

  8. 【OpenGL ES】立方体贴图(6张图)

    1 前言 本文通过一个立方体贴图的例子,讲解三维纹理贴图的应用,案例中使用 6 张不同的图片给立方体贴图,图片如下: 本文涉及到的知识点主要包含:三维绘图.MVP 矩阵变换.纹理贴图,读者如果对 Op ...

  9. opengl android 纹理贴图 代码,Android 使用opengl es的纹理贴图白屏问题请教。

    各位大侠好: 我在使用open gl es的做显示的时候,发现一个问题,请各位帮助一下,谢谢. 环境:opengl es 1.x,2D的模式显示纹理图片. 在LG-P990,HTC-C510E上显示附 ...

最新文章

  1. arcpy实现空间查询_布隆过滤!Python实现亿级数据集中元素快速查找
  2. Swift初探 1 helloWord
  3. Oracle RAC CSS 超时计算 及 参数 misscount, Disktimeout 说明
  4. 基于 RT-Thread的麦克纳姆轮小车循迹运动控制算法开发和研究
  5. ASP生成HTML讲座笔记
  6. 快速学习23种设计模式思想Design Patterns
  7. centos6.5卸载和安装mysql_Linux CentOS 6.5 卸载、tar安装MySQL的教程
  8. Linux单机安装kafka
  9. hdu 1760 DFS+博弈
  10. Android 系统(225)---Android 7.0切换阿拉伯语,QuickSetting界面图标左右翻转
  11. TCP三次握手连接和TCP四次挥手及大量TIME_WAIT解决方法:
  12. 卡特兰(Catalan)数列
  13. deepsort报错 No module named ‘sklearn.utils.linear_assignment_‘ 问题解决
  14. 关于时间、日期的一些应用
  15. Microsoft Sql Server 2008 R2 Express 下载地址推荐
  16. 注解的引入以及注解的使用
  17. java微信小程序毕业设计 java微信活动报名志愿者小程序系统毕业设计开题报告参考
  18. 抖音快手短视频去水印API,接口开发文档
  19. 台式计算机diy,DIY组装台式电脑经验分享学习篇
  20. Vscode Opencv4.5.2环境搭建

热门文章

  1. centos查看磁盘转速_Centos磁盘读写检测进行性能判断
  2. 安全日志分析的五种类型
  3. 光猫连接水星路由器显示服务器,水星mw300r路由器连接光猫的设置方法步骤
  4. 页面置换算法(FIFO、第二次机会、LRU)
  5. Origin绘图—如何添加参考线
  6. GDI+ 画路径(消除锯齿的效果很不错)
  7. 这一年,你遇见了谁?
  8. MODTRAN辐射传输模型使用笔记
  9. opencv分离RGB三通道
  10. Python分布式爬虫打造搜索引擎