【转】OpenGL超级宝典笔记——纹理映射Mipmap

原文地址 http://my.oschina.net/sweetdark/blog/177812 , 感谢作者,若非法转载请联系本人。

目录[-]

  • Mipmapping
  • Mipmap过滤
  • 构建Mip层
  • Mipmaps 硬件生成
  • LOD(多细节层次)偏好
  • 纹理对象
  • 管理多个纹理
  • 常驻纹理
  • 纹理优先级
  • 回顾

Mipmapping

Mipmap是一个功能强大的纹理技术,它可以提高渲染的性能以及提升场景的视觉质量。它可以用来解决使用一般的纹理贴图会出现的两个常见的问题:

  • 闪烁,当屏幕上被渲染物体的表面与它所应用的纹理图像相比显得非常小时,就会出现闪烁。尤其当相机和物体在移动的时候,这种负面效果更容易被看到。
  • 性能问题。加载了大量的纹理数据之后,还要对其进行过滤处理(缩小),在屏幕上显示的只是一小部分。纹理越大,所造成的性能影响就越大。

Mipmap就可以解决上面那两个问题。当加载纹理的时候,不单单是加载一个纹理,而是加载一系列从大到小的纹理当mipmapped纹理状态中。然后OpenGl会根据给定的几何图像的大小选择最合适的纹理。Mipmap是把纹理按照2的倍数进行缩放,直到图像为1x1的大小,然后把这些图都存储起来,当要使用的就选择一个合适的图像。这会增加一些额外的内存。在正方形的纹理贴图中使用mipmap技术,大概要比原先多出三分之一的内存空间。

mipmap有多少个层级是有glTexImage的第二个参数level决定的。层级从0开始,0,1,2,3这样递增。如果没有使用mipmap技术,只有第0层的纹理会被加载。在默认情况下,为了使用mipmap,所有层级都会被加载。但我们可以通过纹理参数来控制要加载的层级范围,使用glTexParameteri, 第二个参数为GL_TEXTURE_BASE_LEVEL来指定最低层级的level,第二个参数为GL_TEXTURE_MAX_LEVEL指定最高层级的level。例如我只需要加载0到4层级的纹理:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);

除此之外,我们还可通过GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD来限制纹理的使用范围(最底层和最高层)。

Mipmap过滤

Mipmap的纹理过滤模式如下表:

常量描述
GL_NEAREST在mip基层上使用最邻近过滤
GL_LINEAR在mip基层上使用线性过滤
GL_NEAREST_MIPMAP_NEAREST选择最邻近的mip层,并使用最邻近过滤
GL_NEAREST_MIPMAP_LINEAR在mip层之间使用线性插值和最邻近过滤
GL_LINEAR_MIPMAP_NEAREST选择最邻近的mip层,使用线性过滤
GL_LINEAR_MIPMAP_LINEAR在mip层之间使用线性插值和使用线性过滤,又称三线性mipmap

如果纹理过滤选择为GL_NEAREST或GL_LINEAR模式,那么只有基层的纹理会被加载,其他的纹理将会被忽略。我们必须指定其中一个mipmap过滤器,这样才能使用所有已加载的纹理。这个mipmap过滤器的常量是GL_FILTER_MIPMAP_SELECTOR的形式。其中FLILTER指定了过滤模式,SELECTOR指定了如何选择mipmap层。例如GL_NEAREST_MIPMAP_LINEAR模式,它的SELECTOR是GL_LINEAR,它会在两个最邻近的mip层中执行线性插值,然后得出的结果又由被选择的过滤器GL_NEAREST进行过滤。

其中GL_NEAREST_MIPMAP_NEAAREST具有很好的性能,也能够解决闪烁的问题,但在视觉效果上会比较差。其中GL_LINEAR_MIPMAP_NEAREST常用于游戏加速,使用了质量较高的线性过滤,和快速的选择的方式(最邻近方式)。

使用最邻近的方式作为mipmap选择器的效果依然不能令人满意。从某一个角度去看,常常可以看到物体表面从一个mip层到另一个mip层的转变。GL_LINEAR_MIPMAP_LINEAR和GL_NEAREST_MIPMAP_LINEAR过滤器在mip层之间执行一些额外的线性插值,以消除不同层之间的变换痕迹,但也需要一些额外的性能开销。GL_LINEAR_MIPMAP_LINEAR具有最高的精度。

构建Mip层

mip贴图需要加载更小的基本纹理图像以便使用。但我们手头上没有这些更小的纹理图像,怎么办呢。GLU函数库提供了一个很方便的方法gluBuildMipmaps,它会帮我们缩放图像并通过类似glTexImage的函数加载图像。支持1维、2维、3维的图像,函数原型如下:

int gluBuild1DMipmaps(GLenum target, GLint internalFormat, GLint width, GLenum format, GLenum type, const void *data);

int gluBuild2DMipmaps(GLenum target, GLint internalFormat, GLint width, GLint height, GLenum format, GLenum type, const void *data);

int gluBuild3DMipmaps(GLenum target, GLint internalFormat, GLint width, GLint height, GLint depth, GLenum format, GLenum type, const void *data);

参数的意义与glTexImage相同。但没有level参数来指定mipmap的层级,也不支持纹理边界。使用这个函数未必能够获得高质量的较小的纹理贴图,只是比较方便。要使用高质量的不同比例的纹理贴图,最好是自己手工制作,然后加载。GLU库是使用box过滤器(简单地就是对给定范围的像素进行加权平均,例如7X7的box filter,你就需要对49个像素进行平均)

新版的GLU库中可以使用gluBuild*MipmapLevels来更好的控制加载的纹理层级

int gluBuild1DMipmapLevels(GLenum target, GLint internalFormat, GLint width, GLenum format, GLenum type, GLint base, GLint max, const void *data);

int gluBuild2DMipmapLevels(GLenum target, GLint internalFormat, GLint width, GLint height, GLenum format, GLenum type, GLint base, GLint max, const void *data);

int gluBuild3DMipmapLevels(GLenum target, GLint internalFormat, GLint widht, GLint height, GLint depth, GLenum format, GLenum type, GLint base, GLint max, const void *data);

创建从base到max层的纹理数据。

Mipmaps 硬件生成

使用OpenGL的硬件加速来生成所需要的纹理。函数调用如下:

glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);

当这个参数被设置为GL_TRUE时,所有调用glTexImage或者glTexSubImage都会自动更新纹理贴图(第0级)和所有更低层的纹理。通过使用硬件加速会比使用glu库中的gluBuildMipmap要快,但这个特性本来只是个扩展,在OpenGL1.4才被纳入OpenGL核心API的。

LOD(多细节层次)偏好

当mipmapping被启用时,OpenGL会根据各个mip层的大小和几何图形在屏幕上的面积来决定哪一个mip层被选择。OpenGL会选择最合适的mip贴图层与屏幕上的纹理表示形式进行匹配。我们可以告诉OpenGL向后(选择更大的mip层)或向前(选择更小的mipmap层)来调整选择的标准。使用更小的mip层可以提高性能,选择更大的mip层可以锐化纹理映射的对象。这个偏好设置示例如下:

glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, –1.5);

上面会使的细节纹理层倾向于使用更高层的细节(更小的level层参数),从而使得纹理的外观更为锐利,代价是纹理处理的开销大一些。

纹理对象

glTexImage, glTexSubImage和gluBuildMipmaps这些函数的调用消耗的时间特别多。这些函数大量的移动内存,有时需要重新调整数据的格式来适应一些内部的表示。在纹理之间切换或者重新加载不同的纹理图片会带来较大的性能开销。

为了减少这些开销,我们可以使用纹理对象。纹理对象允许你一次性加载多个纹理状态(包括纹理图像),然后在它们之间快速切换。纹理状态由当前绑定的纹理对象来维护。纹理的名称由unsigned int类型来标识。使用下面的函数来生成一定数量的纹理对象:

void glGenTextures(GLsizei n, GLuint *textures);

上面的函数调用指定了纹理对象的数量,和存储纹理对象名称的数组。我们可以通过纹理对象名称来操作纹理状态。绑定其中的一个纹理状态的函数调用如下:

void glBindTexture(GLenum target, GLuint texture);

target参数必须是GL_TEXTURE_1D,GL_TEXTURE_2D或者GL_TEXTURE_3D.texture是纹理名称指定要绑定的纹理对象。在此函数之后,纹理图像的加载和纹理参数的设置都只影响当前绑定的纹理对象。最后删除纹理对象的函数如下:

void glDeleteTextures(GLsizei n, GLuint *texture);

参数的意义与glGenTextures相同。不一定需要每次产生纹理对象使用后就删除所有的纹理对象。多次调用glGenTextures的开销较小,但多次调用glDeleteTextures会有导致一些延迟,原因是需要释放大量的能存空间。在不再需要此纹理对象时,要把该纹理对象删除,防止内存泄露。

判断纹理对象名称是否可用可以通过下面的函数调用来判断:

GLboolean glIsTexture(GLuint texture);

返回GL_TRUE代表可用,GL_FALSE代表不可用。

管理多个纹理

一般而言,在程序初始化时加载多个纹理对象,然后在渲染期间不断地切换,在不再使用时删除纹理对象。下面是一个通道tunnel的例子,此例在启动时加载三个纹理对象,然后通过切换来绘制通道的砖墙,天花板和地板。此例中还演示了不同的mipmap模式,通过右键菜单来切换,通过上下箭头键来在通道中移动,通过左右键来旋转通道。

完整示例如下:

#include "gltools.h"
#include <stdio.h>//定义宏常量
#define CEILING 0
#define BRICK 1
#define FLOOR 2
#define TEXTURENUM 3//纹理图像的路径
const char* texFileName[] = {"..\\ceiling.tga","..\\brick.tga","..\\floor.tga"};//纹理对象名称
static GLuint textureName[TEXTURENUM];//旋转与移动
static GLfloat yRot = 0.0f;
static GLfloat zPos = 0.0f;//切换不同的纹理模式
void ProcessMenu(int value)
{switch (value){case 0:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);break;case 1:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);break;case 2:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);break;case 3:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);break;case 4:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);break;case 5:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);break;case 6:if (gltIsExtSupported("GL_EXT_texture_filter_anisotropic")){//开启各向异性过滤
      GLfloat fLargest;glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);printf("anisotropic:%f\n", fLargest);glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);}break;default:break;}glutPostRedisplay();
}void SetupRC()
{glClearColor(0.0f, 0.0f, 0.0f, 1.0f);//开启深度测试,消除隐藏面,避免后画的墙画到前面来
  glEnable(GL_DEPTH_TEST);//纹理图像的信息
  GLint iWidth, iHeight, iComponents;GLenum eFormat;//设置纹理环境
  glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV, GL_REPLACE);//生成纹理对象
  glGenTextures(TEXTURENUM, textureName);for (int i = 0; i < TEXTURENUM; ++i){void *pImage = gltLoadTGA(texFileName[i], &iWidth, &iHeight, &iComponents, &eFormat);if (pImage){//绑定纹理对象,生成mipmap
      glBindTexture(GL_TEXTURE_2D, textureName[i]);gluBuild2DMipmaps(GL_TEXTURE_2D, iComponents, iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);}free(pImage);}glEnable(GL_TEXTURE_2D);}void ShutdownRC()
{//最后删除纹理对象
  glDeleteTextures(TEXTURENUM, textureName);
}void RenderScene()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glPushMatrix();//移动和旋转glTranslatef(0.0f, 0.0f, zPos);glRotatef(yRot, 0.0f, 1.0f, 0.0f);for(GLfloat z = -60.0f; z <= 0.0f; z += 10.0f){//绑定地板纹理绘制地板,注意glBeindTexture在glBegin和glEnd中是无效的
      glBindTexture(GL_TEXTURE_2D, textureName[FLOOR]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z + 10.0f);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, -10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, -10.0f, z);glEnd();//绑定天花板纹理
      glBindTexture(GL_TEXTURE_2D, textureName[CEILING]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z + 10.0f);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, 10.0f, z);glEnd();//绑定砖墙的纹理
      glBindTexture(GL_TEXTURE_2D, textureName[BRICK]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z);glTexCoord2f(1.0f, 1.0f);glVertex3f(-10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(-10.0f, -10.0f, z + 10.0f);glTexCoord2f(0.0f, 0.0f);glVertex3f(10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(10.0f, 10.0f, z);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, -10.0f, z + 10.0f);glEnd();}//GLclampf prioritize[TEXTURENUM] = {0.0f, 0.0f, 1.0f};//glPrioritizeTextures(TEXTURENUM, textureName, prioritize);//GLboolean isResident[TEXTURENUM];//if (glAreTexturesResident(TEXTURENUM, textureName, isResident))//{//  printf("all texture is resident\n");//}//else//{//  printf("texture resident is : %d %d %d", isResident[0], isResident[1], isResident[2]);//}
  glPopMatrix();glutSwapBuffers();
}void ChangeSize(GLsizei w, GLsizei h)
{if (h == 1)h = 0;glViewport(0, 0, w, h);GLfloat aspect = (GLfloat)w/(GLfloat)h;glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(35.5, aspect, 1.0, 150.0);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glutPostRedisplay();
}void SpecialKey(int value, int x, int y)
{if (value == GLUT_KEY_LEFT){yRot += 0.5f;}if (value == GLUT_KEY_RIGHT){yRot -= 0.5f;}if (value == GLUT_KEY_UP){zPos += 0.5f;}if (value == GLUT_KEY_DOWN){zPos -= 0.5f;}if (yRot > 365.5f){yRot = 0.0f;}glutPostRedisplay();
}int main(int arg, char **argv)
{glutInit(&arg, argv);glutInitDisplayMode(GL_RGB | GL_DOUBLE | GL_DEPTH);glutInitWindowSize(800, 600);glutCreateWindow("tunel");glutReshapeFunc(ChangeSize);glutDisplayFunc(RenderScene);glutSpecialFunc(SpecialKey);glutCreateMenu(ProcessMenu);glutAddMenuEntry("GL_NEAREST", 0);glutAddMenuEntry("GL_LINEAR", 1);glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST", 2);glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 3);glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 4);glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);glutAddMenuEntry("ANISOTROPIC", 6);glutAttachMenu(GLUT_RIGHT_BUTTON);SetupRC();glutMainLoop();ShutdownRC();return 0;
}

 

GL_NEAREST效果图(纹理比较锐利):

image

GL_LINEAR_MIPMAP_LINEAR效果图(线性插值后的纹理过渡较平滑):

image

在我们程序初始化时,生成了多个纹理对象,加载了多个纹理。这样我们在使用时就非常的方便,只要通过glBindTexutre切换纹理就可以了。使用完之后就释放纹理。

for(GLfloat z = -60.0f; z <= 0.0f; z += 10.0f){//绑定地板纹理绘制地板,注意glBeindTexture在glBegin和glEnd中是无效的
      glBindTexture(GL_TEXTURE_2D, textureName[FLOOR]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z + 10.0f);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, -10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, -10.0f, z);glEnd();//绑定天花板纹理
      glBindTexture(GL_TEXTURE_2D, textureName[CEILING]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z + 10.0f);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, 10.0f, z);glEnd();//绑定砖墙的纹理
      glBindTexture(GL_TEXTURE_2D, textureName[BRICK]);glBegin(GL_QUADS);glTexCoord2f(0.0f, 0.0f);glVertex3f(-10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(-10.0f, 10.0f, z);glTexCoord2f(1.0f, 1.0f);glVertex3f(-10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(-10.0f, -10.0f, z + 10.0f);glTexCoord2f(0.0f, 0.0f);glVertex3f(10.0f, -10.0f, z);glTexCoord2f(1.0f, 0.0f);glVertex3f(10.0f, 10.0f, z);glTexCoord2f(1.0f, 1.0f);glVertex3f(10.0f, 10.0f, z + 10.0f);glTexCoord2f(0.0f, 1.0f);glVertex3f(10.0f, -10.0f, z + 10.0f);glEnd();}

 

tunnel示例中,切换mipmap纹理过滤器时,只修改了缩小过滤器

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

典型的情况下,在OpenGL选择了最大的可用mip层之后,就没有更大的mip层可供选择了。这相当于设置了一条门槛,更大的图形也只能使用这个mip层,没有更大的mip层了。

常驻纹理

在大多数的OpenGL实现中,都提供了一定数量的常驻纹理来提高性能。显卡的内存(显存)是有限的,在纹理切换中当显存不够时,就要把一些纹理数据从显存中移除(暂时存放到硬盘或内存中)。

为了优化性能,OpenGL会自动的把最经常使用的纹理保存在显存中作为常驻纹理。判断当前纹理是否是常驻纹理,通过下面的函数调用来测试:

GLboolean glAreTexturesResident(GLsizei n, const GLuint *texture, GLboolean *residences);

第一个参数是纹理对象的个数,第二是纹理对象名称数组,第三个是输出参数,记录着对应的纹理是否是常驻纹理。如果所有的纹理对象都是常驻纹理则glAreTexutresResident返回GL_TRUE。

纹理优先级

默认情况下,OpenGL实现是使用最经常使用(MFFU)来决定纹理是否常驻显存。如果最经常使用的纹理很小,而大纹理不经常使用,那么就会导致小纹理常驻显存,而大纹理却经常被移除。为了避免此问题,我们可以手动设置纹理的优先级。通过下面的函数调用:

void glPrioritizeTextures(GLsizei n, const GLuint *texture, const GLclampf *priorites);

第一个参数是纹理对象的个数,第二个是纹理名称数组,第三个是纹理的优先级参数,范围为[0.0,1.0]。低优先级告诉OpenGL当显存不够时有限考虑移除此低优先级的纹理。示例:

GLclampf prioritize[TEXTURENUM] = {0.0f, 0.0f, 1.0f};
glPrioritizeTextures(TEXTURENUM, textureName, prioritize);
GLboolean isResident[TEXTURENUM];
if (glAreTexturesResident(TEXTURENUM, textureName, isResident))
{
printf("all texture is resident\n");
}
else
{
printf("texture resident is : %d %d %d", isResident[0], isResident[1], isResident[2]);
}

回顾

在纹理这一章,我们学习如何加载纹理glTexImage,设置纹理参数glTexParameter,设置纹理环境glTexEnv,使用mipmap,管理纹理对象,多纹理的使用和切换,纹理过滤器等等。详细的介绍了纹理的各种参数。

代码以及数据纹理和库都在此地址可以获取(上面的示例为tunnel工程):

https://github.com/sweetdark/openglex

转载于:https://www.cnblogs.com/cack/p/4972806.html

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

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

相关文章

【Microstation】第二章:Microstation三维建模基础知识

本章的主要内容包括模型的显示样式(线框、光滑)、三维定位(V、T、S、F)、Microstation常见的坐标系统(世界坐标系、ACS辅助坐标系、精确绘图坐标系、)和Microstation的工作区域(2D和3D)。 一、显示样式 二、三维定位 三维定位在Microstation中显得尤为重要,常见…

实现 EF Core 6 自定义查询标记

前言在《EF Core使用Simple Logging输出日志》中&#xff0c;我们介绍了查询标记 TagWith&#xff0c;它可以帮助我们快速定位到需要的日志&#xff1a;而在 .NET 6 中&#xff0c;新增了另外一个查询标记 TagWithCallSite&#xff0c;它可以标记出代码的位置&#xff1a;var u…

0运维?微信小程序云开发增删查改【05】

在创建小程序时&#xff0c;选择云开发&#xff1a; 随后进入项目之后&#xff0c;此时整个目录如下&#xff1a; 此时我们如图目录即可找到首页位置&#xff1a; 接着咱们清除 index.wxml 代码内容&#xff1a; 在 index.wxml 中加入如下代码&#xff1a; <view> …

【Microstation】第三章:Microstation三维模型构建与编辑

本章主要讲述三维基本实体绘制、三维构造元素绘制、三维模型编辑。 一、三维基本体素绘制 对于立方体、圆柱、球、圆锥等这些基本立体单位,MS提供了专门的绘图工具。 基本体素绘制有两种方式: (1)精确绘图工具 (2&

文件系统管理相关命令

查看文件系统相关属性的命令&#xff1a;blkidblkid是一个查看磁盘设备属性相关信息的命令行工具blkid -L LABEL | UUID :根据UUID查看对应的设备是哪个blkid [-ghlv] [-c file] [-w file] [-o format][-s tag] [-t NAMEvalue] device [device ...]-i&#xff1a;显示io限制lsb…

Android之实现夸克浏览器书签和历史页面滑动时候右上角图标切换效果

1 需求 实现夸克浏览器书签和历史页面滑动时候右上角图标切换效果,页面滑动的时候,图标也左右滑动,但是只是显示其中的一个 https://www.captainai.net/st/ 2 代码实现 xml布局实现 <LinearLayoutandroid:id="@+id/mainLl"android:layout_width="24d…

你都用 Python 来做什么?

你们都用python做些什么呢&#xff1f; 在开发中 python 这一个语言就像是小叮当&#xff0c;而 python 的第三方库则是“百宝箱”&#xff0c;你只要想着对某一个方向进行开发&#xff0c;那么这个“百宝箱”就会给你想要的东西。 由于我是在开发多年后接触到的 python&#…

Android之tint图片着色器

1、爆照 上面是原图,下面是点击效果。 2、介绍 设置着色模式用的。这个模式共有6种,分别为: multiply screen src_in(默认) src_over src_atop add android:tint 属性可以改变图片颜色 3 源代码 colors.xml <?xml version="1.0" encoding="utf-8&qu…

关于 Oracle分页数据重复的问题

2019独角兽企业重金招聘Python工程师标准>>> 先说问题吧。最近在测试一个新的模块&#xff0c;发现列表数据的前三页数据竟然是一样的。第一反应是 pageNo 的问题&#xff0c;debug一看&#xff0c;pageNo是正确的&#xff0c;然后一层层debug下去&#xff0c;所有参…

10分钟做一个新闻问答web站点[iVX低代码实战]

一、创建首页 进入到iVX线上编辑器后&#xff0c;选择相对定位。 点击创建后进入到 IDE 之中&#xff1a; 我们在第一步中首先创建一个首页。点击左侧组件栏中的页面组件&#xff0c;创建一个页面&#xff1a; 接着重命名该页面为Home&#xff0c;在页面中创建一个行&#x…

iOS开发网络篇—网络编程基础

一、为什么要学习网络编程 1.简单说明 在移动互联网时代&#xff0c;移动应用的特征有&#xff1a; &#xff08;1&#xff09;几乎所有应用都需要用到网络&#xff0c;比如QQ、微博、网易新闻、优酷、百度地图 &#xff08;2&#xff09;只有通过网络跟外界进行数据交互、数据…

深入理解Three.js(WebGL)贴图(纹理映射)和UV映射

本文将详细描述如何使用Three.js给3D对象添加贴图&#xff08;Texture Map&#xff0c;也译作纹理映射&#xff0c;“贴图”的翻译要更直观&#xff0c;而“纹理映射”更准确。&#xff09;。为了能够查看在线演示效果&#xff0c;你需要有一个兼容WebGL的现代浏览器&#xff0…

在兰州吃牛肉面时亲眼目睹的一幕,我感动了

今天中午去吃占国牛肉面&#xff0c;人比较多&#xff0c;很热闹。旁边坐了一群身穿校服的小学生&#xff0c;身后坐着一位小学老师&#xff0c;当孩子们发现老师在后面的时候&#xff0c;都异口同声地叫老师过去跟他们一起吃&#xff0c;老师嘿的一笑&#xff0c;没同意&#…

如何跨 Namespace 同步 Secret 和 ConfigMap?

Secret 和 ConfigMap 资源对象是命名空间级别的。它们只能被同一命名空间中的 Pod 引用。所以有时候不得不手动为每个命名空间创建它们。但有很多场景&#xff0c;我们想让它们是全局的&#xff0c;至少可以是跨命名空间共享的 Secret 和 ConfigMap&#xff0c;例如这些场景&am…

一文总结学习 Python 的 14 张思维导图

本文主要涵盖了 Python 编程的核心知识&#xff08;暂不包括标准库及第三方库&#xff0c;后续会发布相应专题的文章&#xff09;。 首先&#xff0c;按顺序依次展示了以下内容的一系列思维导图&#xff1a;基础知识&#xff0c;数据类型&#xff08;数字&#xff0c;字符串&am…

GEE学习笔记

掩膜 ​ 在遥感图像处理中&#xff0c;"掩膜"是指一种用于隐藏或保留图像特定部分的技术。掩膜通常是一个二进制图像&#xff0c;其中的像素值为0或1&#xff0c;分别表示遮蔽或保留。 ​ 在去除云的情境中&#xff0c;掩膜通常用于隐藏图像中被云覆盖的部分&#…

十一、飞机大战(IVX 快速开发教程)

十一、飞机大战 制作微信小游戏大致流程与微信小程序、Web类似&#xff0c;不同的在于是组件的使用。 文章目录十一、飞机大战11.1.1 完成游戏角色制作11.1.2 完成物理世界添加11.1.3 完成子弹对象反重力运动11.1.4 使用对象组创建子弹11.1.5 子弹优化11.1.6 设置敌机11.1.7 优…

中国版LinkedIn呼之欲出

迄今为止&#xff0c;发展势头强劲的Facebook仍未上市&#xff0c;而美国职业社交网站LinkedIn 却率先登录纽约证券交易所&#xff0c;股价在第一天的交易中飙升&#xff0c;从45美元的发行价上涨了一倍&#xff0c;然后超过100美元&#xff0c;最终以94.25美元收盘。 LinkedIn…

Android之实现RecyclerView拖拽效果和固定部分元素不进行交换位置(包含源代码下载地址)

1、效果爆照 启动的效果 拖动过程中的效果 拖动后的效果 2、需求和问题 需求:RecyclerView实现拖拽效果,但是部分固定位置不能进行拖拽也不能在拖拽的过程中交换顺序

各主流浏览器内核介绍

所谓的“浏览器内核”无非指的是一个浏览器最核心的部分——“Rendering Engine”&#xff0c;直译这个词汇叫做“渲染引擎”&#xff0c;不过我们也常称其为“排版引擎”、“解释引擎”。这个引擎的作用是帮助浏览器来渲染网页的内容&#xff0c;将页面内 容和排版代码转换为用…