从0开发游戏引擎之纹理管理器实现 纹理数据绑定OpenGL滤波方式选择线性滤波

简介: 从0开发游戏引擎之纹理管理器实现 纹理数据绑定OpenGL滤波方式选择线性滤波

这一章主要是讲如何把一张图片加载进内存中转换BGR->RGB格式,然后和OpenGL做绑定渲染出来。


这一章讲解的顺序完全反过来了。基础好的读者也可以反过来看,先看重点,然后再回过头来看源码。没接触过的读者可以先简单的读一遍源码,然后再看重点分析,这样看重点分析的话会更有感觉。源码的话文章写的差不多了,我再整理一下会放到群里。


先贴出完整代码


TextureManager.h


#pragma once
#include <list>
struct  textureHeight
{
public:
  GLfloat m_iLowHeight=0;//最低高度
  GLfloat m_iHighHeight=0;//最高高度
  GLfloat m_iOptimalHeight=0;//最佳高度
};
class CCTexture
{
  friend class CTextureManager;
private:
  char name[255]; //纹理路径
  GLuint type;  //类型,保存了一个16进制数
  GLuint texID; //纹理ID
  UINT  width;     //图片的宽
  UINT height;  //图片的高
  UINT uReference; //引用计数
  int bytesPerPixel; //每个像素的位(Bit)
  GLubyte *imageData;//纹理数据(unsigned char)
  Vector3D centerPos ={0,0,1};
public:
  textureHeight *Tex=new textureHeight;
  CCTexture();
  CCTexture(const char *fileName,float width=1,float height=1);
  CCTexture(const char * name, int wid, int hei, int BitCount);
  int getbyte() { return bytesPerPixel; }
  GLuint getType() { return type; }
  char *getName() { return name; }
  GLuint getTextureID() { return texID; }
  UINT getWidth() { return width; }
  UINT getHeight() { return height; }
  BOOL saveBmp(const char * fileName);
  GLubyte *getImageData() { return imageData; }
  void setImageData(GLubyte *data) { this->imageData = data; }
  void setCenterPos(Vector3D centerPoint);
  void draw();
  ~CCTexture() ;
public:
};
class CTextureManager
{
private:
  std::list<CCTexture*> textureList;
  static CTextureManager*instance;
public:
  static CTextureManager*getInstance();
  CCTexture* addTexture(char *fileName,float width=1,float height=1);
  CCTexture *addTexture(const char*fileName);
  CCTexture * addTexture(char *name, int width, int height, int pixelFormat);
  void resetRef();//把引用计数全都归零
  void releaseRef();//把引用计数全都归零
  void CreateGLTextures(int wid, int hei, int biBitCount, OUT GLuint &texture, OUT GLubyte*&textureData);
  CTextureManager();
  ~CTextureManager();
};


TextureManager.cpp


#include "Engine.h"
CTextureManager*CTextureManager::instance = NULL;
CCTexture::CCTexture()
{
  texID = 0;
  uReference = 1;
  imageData = NULL;
}
CCTexture::CCTexture(const char* fileName,float wid,float hei)
{
  texID = 0;
  CImage img;
  HRESULT hr = img.Load(CUser::getInstance()->CharToWchar(fileName));
  if (FAILED(hr))
  {
    char temp[256];
    sprintf_s(temp, 256, "%s打开失败", fileName);
    MessageBox(NULL, CUser::getInstance()->CharToWchar(temp), L"错误", 0);
    return;
  }
  strcpy(name, fileName);
  HBITMAP hbmp = img;
  BITMAP bm;
  GetObject(hbmp, sizeof(bm), &bm);
  if (wid > 1 && hei > 1)
  {
    width = wid;  height = hei;
  }
  else 
  {
    width = bm.bmWidth; height = bm.bmHeight;
  }
  bytesPerPixel = bm.bmBitsPixel / 8;
  if (bytesPerPixel == 3)
  {
    type = GL_RGB;
  }
  else if (bytesPerPixel == 4)
  {
    type = GL_RGBA;
  }
  else if (bytesPerPixel == 1)
  {
    type = GL_LUMINANCE;
  }
  imageData = new GLubyte[width*height*bytesPerPixel];
  memcpy(imageData, bm.bmBits, width*height*bytesPerPixel);
  // Convert From BGR To RGB Format And Add An Alpha Value Of 255
  if (bytesPerPixel != 1)
  {
    for (long i = 0; i < width * height; i++)
    {
      GLubyte temp = imageData[0 + i*bytesPerPixel];
      imageData[0 + i*bytesPerPixel] = imageData[2 + i*bytesPerPixel];
      imageData[2 + i*bytesPerPixel] = temp;
    }
  }
  else //等于8位时处理的
  {
    for (int i = 0; i < height; i++)
    {
      memcpy(imageData + i*width, img.GetPixelAddress(0, i), width);
    }
  }
  glGenTextures(1, (GLuint*)&texID);
  glBindTexture(GL_TEXTURE_2D, texID);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//纹理过滤模式
  gluBuild2DMipmaps(GL_TEXTURE_2D, type, width, height, type, GL_UNSIGNED_BYTE, imageData);
  /*glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    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_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, type, width, height, 0, type, GL_UNSIGNED_BYTE, imageData);*/
}
CCTexture::CCTexture(const char *name, int wid, int hei, int BitCount)
{
  uReference =1;
  strcpy(this->name,name);
  bytesPerPixel = BitCount / 8;
  this->width = wid;
  this->height = hei;
  glGenTextures(1, &texID);
  glBindTexture(GL_TEXTURE_2D, texID);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  imageData = new GLubyte[width*height*bytesPerPixel];
  //memset(imageData, 0, width*height*bytesPerPixel);
  if (bytesPerPixel == 1)
  {
    for (int i = 0; i < width*height; ++i)
    {
      imageData[i] = 0;
    }
    type = GL_LUMINANCE;
  }
  else if (bytesPerPixel == 3)
  {
    for (int i = 0; i < width*height; ++i)
    {
      imageData[i * 3 + 0] = 0;
      imageData[i * 3 + 1] = 0;
      imageData[i * 3 + 2] = 0;
    }
    type = GL_RGB;
  }
  else if (bytesPerPixel == 4)
  {
    for (int i = 0; i < width*height; ++i)
    {
      imageData[i * 4 + 0] = 0;
      imageData[i * 4 + 1] = 0;
      imageData[i * 4 + 2] = 0;
      imageData[i * 4 + 3] = 0;
    }
    type = GL_RGBA;
  }
  glTexImage2D(GL_TEXTURE_2D, 0, type, wid, hei, 0, type, GL_UNSIGNED_BYTE, imageData);
}
BOOL CCTexture::saveBmp(const char *fileName)
{
  //如果位图数据指针为0,则没有数据传入,函数返回
  if (!imageData)
    return 0;
  //灰度图
  static RGBQUAD *pColorTable = NULL;
  if (pColorTable == NULL)
  {
    pColorTable = new RGBQUAD[256];
    for (int i = 0; i < 256; i++)
    {
      pColorTable[i].rgbBlue = i;
      pColorTable[i].rgbGreen = i;
      pColorTable[i].rgbRed = i;
      pColorTable[i].rgbReserved = 0;
    }
  }
  //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
  int colorTablesize = 0;
  if (bytesPerPixel == 1)
    colorTablesize = 1024;
  //待存储图像数据每行字节数为4的倍数
  int lineByte = (width * bytesPerPixel + 3) / 4 * 4;
  //以二进制写的方式打开文件
  FILE *fp = fopen(fileName, "wb");
  if (fp == 0) return 0;
  //申请位图文件头结构变量,填写文件头信息
  BITMAPFILEHEADER fileHead;
  fileHead.bfType = 0x4D42;//bmp类型
               //bfSize是图像文件4个组成部分之和
  fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
    + colorTablesize + lineByte*height;
  fileHead.bfReserved1 = 0;
  fileHead.bfReserved2 = 0;
  //bfOffBits是图像文件前三个部分所需空间之和
  fileHead.bfOffBits = 54 + colorTablesize;
  //写文件头进文件
  fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
  //申请位图信息头结构变量,填写信息头信息
  BITMAPINFOHEADER head;
  head.biBitCount = bytesPerPixel * 8;
  head.biClrImportant = 0;
  head.biClrUsed = 0;
  head.biCompression = 0;
  head.biHeight = height;
  head.biPlanes = 1;
  head.biSize = 40;
  head.biSizeImage = lineByte*height;
  head.biWidth = width;
  head.biXPelsPerMeter = 0;
  head.biYPelsPerMeter = 0;
  //写位图信息头进内存
  fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
  //如果灰度图像,有颜色表,写入文件
  if (bytesPerPixel == 1)
    fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
  //写位图数据进文件
  if (bytesPerPixel == 1)
  {
    fwrite(imageData, height*lineByte, 1, fp);
  }
  else
  {
    GLubyte *newData = new GLubyte[width * height*bytesPerPixel];
    memcpy(newData, imageData, width * height*bytesPerPixel);
    for (long i = 0; i < width * height; i++)
    {
      GLubyte temp = newData[0 + i*bytesPerPixel];
      newData[0 + i*bytesPerPixel] = newData[2 + i*bytesPerPixel];
      newData[2 + i*bytesPerPixel] = temp;
    }
    fwrite(newData, height*lineByte, 1, fp);
    delete[]newData;
  }
  //关闭文件
  fclose(fp);
  return 1;
}
void CCTexture::setCenterPos(Vector3D center)
{
  this->centerPos = center;
}
void CCTexture::draw()
{
  float halfWid = width / 2;
  float halfHei = height / 2;
  Vector3D temp = centerPos;
  glEnable(GL_TEXTURE_2D);
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glBindTexture(GL_TEXTURE_2D, texID);
  glBegin(GL_QUADS);
  glTexCoord2f(0, 0); glVertex3f(centerPos.x- halfWid, centerPos.y+halfHei, 1);
  glTexCoord2f(1, 0); glVertex3f(centerPos.x+ halfWid, centerPos.y+halfHei, 1);
  glTexCoord2f(1, 1); glVertex3f(centerPos.x + halfWid, centerPos.y-halfHei, 1);
  glTexCoord2f(0, 1); glVertex3f(centerPos.x -halfWid, centerPos.y-halfHei, 1);
  glEnd();
  glDisable(GL_TEXTURE_2D);
  glDisable(GL_BLEND);
}
CCTexture::~CCTexture()
{
  glDeleteTextures(1,&texID);
  delete imageData;
}
CTextureManager::CTextureManager()
{
}
CTextureManager*CTextureManager::getInstance()
{
  if (instance == NULL)
  {
    instance = new CTextureManager;
  }
  return instance;
}
CCTexture* CTextureManager::addTexture(char *filename,float width, float height)
{
  list<CCTexture*>::iterator iter;
  for (iter = textureList.begin(); iter != textureList.end(); iter++)
  {
    if (strcmp((*iter)->getName(), filename) == 0)
    {
      (*iter)->uReference++;
      return *iter;
    }
  }
  textureList.push_back(new CCTexture(filename));
  return textureList.back();
}
CCTexture* CTextureManager::addTexture(const char *filename)
{
  const char*lp = strrchr(filename, '.');
  char temp[255];
  if(strcmp(lp,".bmp")==0||strcmp(lp,".jpg")==0)
  {
    strcpy(temp, filename);
  }
  else 
  {
    memset(temp, 0, 255);
    strncpy(temp,filename,lp-filename);
    strcat(temp, ".png");
  }
  list<CCTexture*>::iterator iter;
  for (iter = textureList.begin(); iter != textureList.end(); iter++)
  {
    if (strcmp((*iter)->getName(), filename) == 0)
    {
      (*iter)->uReference++;
      return *iter;
    }
  }
  textureList.push_back(new CCTexture(temp));
  return textureList.back();
}
CCTexture* CTextureManager::addTexture(char *name, int width, int height, int BitCount)
{
  list<CCTexture*>::iterator iter;
  for (iter = textureList.begin(); iter != textureList.end(); iter++)
  {
    if (strcmp((*iter)->getName(), name) == 0)
    {
      (*iter)->uReference++;
      return *iter;
    }
  }
  textureList.push_back(new CCTexture(name, width, height, BitCount));
  return textureList.back();
}
void CTextureManager::releaseRef()
{
  for (auto iter = textureList.begin(); iter != textureList.end();)
  {
    if ((*iter)->uReference == 0)
    {
      delete(*iter);
      textureList.erase(iter++);
    }
    else
      iter++;
  }
}
void CTextureManager::resetRef()
{
  for (auto iter = textureList.begin(); iter != textureList.end(); iter++)
  {
    (*iter)->uReference = 0;
  }
}
void CTextureManager::CreateGLTextures(int wid, int hei, int biBitCount, OUT GLuint &texture, OUT GLubyte*&textureData)
{
  glGenTextures(1, &texture);
  glBindTexture(GL_TEXTURE_2D, texture);
  if (biBitCount == 8)
  {
    textureData = new GLubyte[wid*hei];
    memset(textureData, 0, wid*hei);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   // 设置过滤器为线性过滤
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 1, wid, hei, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, textureData);
  }
  else if (biBitCount == 24)
  {
    textureData = new GLubyte[wid*hei * 3];
    memset(textureData, 0, wid*hei * 3);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);   // 设置过滤器为线性过滤
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, wid, hei, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData);
  }
}
CTextureManager::~CTextureManager()
{
  for (auto iter = textureList.begin();iter != textureList.end();)
  {
    delete (*iter);
    textureList.erase(iter++);
  }
  textureList.clear();
}


保存BMP格式图片(只能保存BMP)


这个函数当时主要是能把编辑器里刷的地形保存出去使用的。


1.BOOL CCTexture::saveBmp(const char *fileName)
{
  //如果位图数据指针为0,则没有数据传入,函数返回
  if (!imageData)
    return 0;
  //灰度图
  static RGBQUAD *pColorTable = NULL;
  if (pColorTable == NULL)
  {
    pColorTable = new RGBQUAD[256];
    for (int i = 0; i < 256; i++)
    {
      pColorTable[i].rgbBlue = i;
      pColorTable[i].rgbGreen = i;
      pColorTable[i].rgbRed = i;
      pColorTable[i].rgbReserved = 0;
    }
  }
  //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
  int colorTablesize = 0;
  if (bytesPerPixel == 1)
    colorTablesize = 1024;
  //待存储图像数据每行字节数为4的倍数
  int lineByte = (width * bytesPerPixel + 3) / 4 * 4;
  //以二进制写的方式打开文件
  FILE *fp = fopen(fileName, "wb");
  if (fp == 0) return 0;
  //申请位图文件头结构变量,填写文件头信息
  BITMAPFILEHEADER fileHead;
  fileHead.bfType = 0x4D42;//bmp类型
               //bfSize是图像文件4个组成部分之和
  fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
    + colorTablesize + lineByte*height;
  fileHead.bfReserved1 = 0;
  fileHead.bfReserved2 = 0;
  //bfOffBits是图像文件前三个部分所需空间之和
  fileHead.bfOffBits = 54 + colorTablesize;
  //写文件头进文件
  fwrite(&fileHead, sizeof(BITMAPFILEHEADER), 1, fp);
  //申请位图信息头结构变量,填写信息头信息
  BITMAPINFOHEADER head;
  head.biBitCount = bytesPerPixel * 8;
  head.biClrImportant = 0;
  head.biClrUsed = 0;
  head.biCompression = 0;
  head.biHeight = height;
  head.biPlanes = 1;
  head.biSize = 40;
  head.biSizeImage = lineByte*height;
  head.biWidth = width;
  head.biXPelsPerMeter = 0;
  head.biYPelsPerMeter = 0;
  //写位图信息头进内存
  fwrite(&head, sizeof(BITMAPINFOHEADER), 1, fp);
  //如果灰度图像,有颜色表,写入文件
  if (bytesPerPixel == 1)
    fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
  //写位图数据进文件
  if (bytesPerPixel == 1)
  {
    fwrite(imageData, height*lineByte, 1, fp);
  }
  else
  {
    GLubyte *newData = new GLubyte[width * height*bytesPerPixel];
    memcpy(newData, imageData, width * height*bytesPerPixel);
    for (long i = 0; i < width * height; i++)
    {
      GLubyte temp = newData[0 + i*bytesPerPixel];
      newData[0 + i*bytesPerPixel] = newData[2 + i*bytesPerPixel];
      newData[2 + i*bytesPerPixel] = temp;
    }
    fwrite(newData, height*lineByte, 1, fp);
    delete[]newData;
  }
  //关闭文件
  fclose(fp);
  return 1;
}


加载文件&绑定纹理选择滤波类型


因为使用的是Windows的图像API加载的,加载进来的图像格式是BGR格式的,要从BGR转换成OpenGL需要的RGB格式。图片过滤方式选择 GL_LINEAR(也叫线性过滤,(Bi)linear Filtering)它会基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。


使用线性过滤的优点与缺点

优点:

       1.质量高:避免了在远距离情况下的采样频率低和数据频率高造成的失真和摩尔纹,效果比无Mipmap好得多。


       2.性能好:避免了不使用Mipmap下距离远时采样频率低和数据频率高而照成texture cache命中率不高(相邻Pixel采样Texel时uv相差比较大)使性能下降。


缺点:

        1.占用显存,可使用ue的纹理流缓存优化(IO换显存)。


CCTexture::CCTexture(const char* fileName,float wid,float hei)
{
  texID = 0;
  CImage img;
  HRESULT hr = img.Load(CUser::getInstance()->CharToWchar(fileName));
  if (FAILED(hr))
  {
    char temp[256];
    sprintf_s(temp, 256, "%s打开失败", fileName);
    MessageBox(NULL, CUser::getInstance()->CharToWchar(temp), L"错误", 0);
    return;
  }
  strcpy(name, fileName);
  HBITMAP hbmp = img;
  BITMAP bm;
  GetObject(hbmp, sizeof(bm), &bm);
  if (wid > 1 && hei > 1)
  {
    width = wid;  height = hei;
  }
  else 
  {
    width = bm.bmWidth; height = bm.bmHeight;
  }
  bytesPerPixel = bm.bmBitsPixel / 8;
  if (bytesPerPixel == 3)
  {
    type = GL_RGB;
  }
  else if (bytesPerPixel == 4)
  {
    type = GL_RGBA;
  }
  else if (bytesPerPixel == 1)
  {
    type = GL_LUMINANCE;
  }
  imageData = new GLubyte[width*height*bytesPerPixel];
  memcpy(imageData, bm.bmBits, width*height*bytesPerPixel);
  // Convert From BGR To RGB Format And Add An Alpha Value Of 255
  if (bytesPerPixel != 1)
  {
    for (long i = 0; i < width * height; i++)
    {
      GLubyte temp = imageData[0 + i*bytesPerPixel];
      imageData[0 + i*bytesPerPixel] = imageData[2 + i*bytesPerPixel];
      imageData[2 + i*bytesPerPixel] = temp;
    }
  }
  else //等于8位时处理的
  {
    for (int i = 0; i < height; i++)
    {
      memcpy(imageData + i*width, img.GetPixelAddress(0, i), width);
    }
  }
  glGenTextures(1, (GLuint*)&texID);
  glBindTexture(GL_TEXTURE_2D, texID);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//纹理过滤模式
  gluBuild2DMipmaps(GL_TEXTURE_2D, type, width, height, type, GL_UNSIGNED_BYTE, imageData);
}


相关文章
|
6月前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
141 1
Android开发之使用OpenGL实现翻书动画
|
6月前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
70 1
Android开发之OpenGL的画笔工具GL10
|
6月前
|
前端开发 API vr&ar
Android开发之OpenGL绘制三维图形的流程
即将连载的系列文章将探索Android上的OpenGL开发,这是一种用于创建3D图形和动画的技术。OpenGL是跨平台的图形库,Android已集成其API。文章以2D绘图为例,解释了OpenGL的3个核心元素:GLSurfaceView(对应View)、GLSurfaceView.Renderer(类似Canvas)和GL10(类似Paint)。通过将这些结合,Android能实现3D图形渲染。文章介绍了Renderer接口的三个方法,分别对应2D绘图的构造、测量布局和绘制过程。示例代码展示了如何在布局中添加GLSurfaceView并注册渲染器。
193 1
Android开发之OpenGL绘制三维图形的流程
|
6月前
|
XML Java Android开发
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
Android App开发中OpenGL三维投影的讲解及实现(附源码和演示 简单易懂)
77 1
从0开发游戏引擎之使用OpenGL绘制三维球体
绘制球体的难点主要在于 要在遍历循环中 根据经纬度反复的使用Cos、Sin函数算出球面上的XYZ三个顶点坐标,一直反复计算,最终三角面多的形成了一个球的形状。
|
监控 API Android开发
音视频开发进阶指南(第四章)-OpenGL-ES显示图片
安卓平台 本节使用OpenGL ES显示PNG图片,基于JNI开发,使用EGL作为OpenGL ES和显示设备之前的桥梁。另外PNG图片的数据读取使用libpng。
225 0
|
6月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
120 0
|
缓存 C++
Opengl ES之FBO
Opengl ES连载系列
144 0
|
存储 编解码 算法
Opengl ES之LUT滤镜(上)
Opengl ES之连载系列
450 0
|
数据安全/隐私保护 开发者
OpenGL ES 多目标渲染(MRT)
Opengl ES连载系列
315 0