android openGL ES详解——混合

一、混合概念 

混合是一种常用的技巧,通常可以用来实现半透明。但其实它也是十分灵活的,你可以通过不同的设置得到不同的混合结果,产生一些有趣或者奇怪的图象。
混合是什么呢?混合就是把两种颜色混在一起。具体一点,就是把某一像素位置原来的颜色和将要画上去的颜色,通过某种方式混在一起,从而实现特殊的效果。   

假设我们需要绘制这样一个场景:透过红色的玻璃去看绿色的物体,那么可以先绘制绿色的物体,再绘制红色玻璃。在绘制红色玻璃的时候,利用“混合”功能,把将要绘制上去的红色和原来的绿色进行混合,于是得到一种新的颜色,看上去就好像玻璃是半透明的。  

在OpenGL中,物体透明技术通常被叫做混合(Blending)。透明是物体(或物体的一部分)非纯色而是混合色,这种颜色来自于不同浓度的自身颜色和它后面的物体颜色。一个有色玻璃窗就是一种透明物体,玻璃有自身的颜色,但是最终的颜色包含了所有玻璃后面的颜色。这也正是混合这名称的出处,因为我们将多种(来自于不同物体)颜色混合为一个颜色,透明使得我们可以看穿物体。

       透明物体可以是完全透明(它使颜色完全穿透)或者半透明的(它使颜色穿透的同时也显示自身颜色)。一个物体的透明度,被定义为它的颜色的alpha值。alpha颜色值是一个颜色向量的第四个元素,你可能已经看到很多了。在这个教程前,我们一直把这个元素设置为1.0,这样物体的透明度就是0.0,同样的,当alpha值是0.0时就表示物体是完全透明的,alpha值为0.5时表示物体的颜色由50%的自身的颜色和50%的后面的颜色组成。

       我们之前所使用的纹理都是由3个颜色元素组成的:红、绿、蓝,但是有些纹理同样有一个内嵌的aloha通道,它为每个纹理像素(Texel)包含着一个alpha值。这个alpha值告诉我们纹理的哪个部分有透明度,以及这个透明度有多少。例如,下面的窗子纹理的玻璃部分的alpha值为0.25(它的颜色是完全红色,但是由于它有75的透明度,它会很大程度上反映出网站的背景色,看起来就不那么红了),角落部分alpha是0.0。

     我们很快就会把这个窗子纹理加到场景中,但是首先,我们将讨论一点简单的技术来实现纹理的半透明,也就是完全透明和完全不透明。

二、忽略片段

      有些图像并不关心半透明度,但也想基于纹理的颜色值显示一部分。例如,创建像草这种物体你不需要花费很大力气,通常把一个草的纹理贴到2D四边形上,然后把这个四边形放置到你的场景中。可是,草并不是像2D四边形这样的形状,而只需要显示草纹理的一部分而忽略其他部分。

       下面的纹理正是这样的纹理,它既有完全不透明的部分(alpha值为1.0)也有完全透明的部分(alpha值为0.0),而没有半透明的部分。你可以看到没有草的部分,图片显示了网站的背景色,而不是它自身的那部分颜色。

所以,当向场景中添加像这样的纹理时,我们不希望看到一个方块图像,而是只显示实际的纹理像素,剩下的部分可以被看穿。我们要忽略(丢弃)纹理透明部分的像素,不必将这些片段储存到颜色缓冲中。在此之前,我们还要学一下如何加载一个带有透明像素的纹理。

加载带有alpha值的纹理我们需要告诉SOIL,去加载RGBA元素图像,而不再是RGB元素的。SOIL能以RGBA的方式加载大多数没有alpha值的纹理,它会将这些像素的alpha值设为了1.0。

unsigned char * image = SOIL_load_image(path, &width, &height, 0, SOIL_LOAD_RGBA);

不要忘记还要改变OpenGL生成的纹理:

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image);

保证你在片段着色器中获取了纹理的所有4个颜色元素,而不仅仅是RGB元素:

void main() 
{ 
// color = vec4(vec3(texture(texture1, TexCoords)), 1.0);
   color = texture(texture1, TexCoords); 
}

由于草纹理被添加到四边形物体上,我们需要再次创建另一个VAO,向里面填充VBO,以及设置合理的顶点属性指针。在我们绘制完地面和两个立方体后,我们就来绘制草叶:

glBindVertexArray(vegetationVAO);
glBindTexture(GL_TEXTURE_2D, grassTexture);  
for(GLuint i = 0; i < vegetation.size(); i++)
{
    model = glm::mat4();
    model = glm::translate(model, vegetation[i]);
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glDrawArrays(GL_TRIANGLES, 0, 6);
}  
glBindVertexArray(0);

运行程序你将看到:

出现这种情况是因为OpenGL默认是不知道如何处理alpha值的,不知道何时忽略(丢弃)它们。我们不得不手动做这件事。幸运的是这很简单,感谢着色器,GLSL为我们提供了discard命令,它保证了片段不会被进一步处理,这样就不会进入颜色缓冲。有了这个命令我们就可以在片段着色器中检查一个片段是否有在一定的阈限下的alpha值,如果有,那么丢弃这个片段,就好像它不存在一样:

#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D texture1;
void main()
{
    vec4 texColor = texture(texture1, TexCoords);
    if(texColor.a < 0.1)
        discard;
    color = texColor;
}

在这儿我们检查被采样纹理颜色包含着一个低于0.1这个阈限的alpha值,如果有,就丢弃这个片段。这个片段着色器能够保证我们只渲染哪些不是完全透明的片段。现在我们来看看效果:

需要注意的是,当采样纹理边缘的时候,OpenGL在边界值和下一个重复的纹理的值之间进行插值(因为我们把它的放置方式设置成了GL_REPEAT)。这样就行了,但是由于我们使用的是透明值,纹理图片的上部获得了它的透明值是与底边的纯色值进行插值的。结果就是一个有点半透明的边,你可以从我们的纹理四边形的四周看到。为了防止它的出现,当你使用alpha纹理的时候要把纹理环绕方式设置为GL_CLAMP_TO_EDGE

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

三、混合API

1、开启和关闭

上述丢弃片段的方式,不能使我们获得渲染半透明图像,我们要么渲染出像素,要么完全地丢弃它。为了渲染出不同的透明度级别,我们需要开启混合(Blending)。

要开启OpenGL的混合功能,只需要调用:glEnable(GL_BLEND);即可。
要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND);即可。
注意:只有在RGBA模式下,才可以使用混合功能,颜色索引模式下是无法使用混合功能的

2、混合方程式

开启混合后,我们还需要告诉OpenGL它该如何混合。

混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理后得到一种新的颜色。这里把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。
OpenGL会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后相加,这样就得到了新的颜色。(也可以不是相加,新版本的OpenGL可以设置运算方式,包括加、减、取两者中较大的、取两者中较小的、逻辑运算等,但我们这里为了简单起见,不讨论这个了)
下面用数学公式来表达一下这个运算方式。假设源颜色的四个分量(指红色,绿色,蓝色,alpha值)是(Rs, Gs, Bs, As),目标颜色的四个分量是(Rd, Gd, Bd, Ad),又设源因子为(Sr, Sg, Sb, Sa),目标因子为(Dr, Dg, Db, Da)。则混合产生的新颜色可以表示为:
(Rs*Sr+Rd*Dr, Gs*Sg+Gd*Dg, Bs*Sb+Bd*Db, As*Sa+Ad*Da)

OpenGL以下面的方程进行混合:


当然了,如果颜色的某一分量超过了1.0,则它会被自动截取为1.0,不需要考虑越界的问题。

注意:
所谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则红色就是源颜色,绿色才是目标颜色。在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕了。

片段着色器运行完成并且所有的测试都通过以后,混合方程才能自由执行片段的颜色输出,当前它在颜色缓冲中(前面片段的颜色在当前片段之前储存)。源和目标颜色会自动被OpenGL设置,而源和目标因子可以让我们自由设置。我们来看一个简单的例子:

我们有两个方块,我们希望在红色方块上绘制绿色方块。红色方块会成为目标颜色(它会先进入颜色缓冲),我们将在红色方块上绘制绿色方块。

那么问题来了:我们怎样来设置因子呢?我们起码要把绿色方块乘以它的alpha值,所以我们打算把FsrcFsrc设置为源颜色向量的alpha值:0.6。接着,让目标方块的浓度等于剩下的alpha值。如果最终的颜色中绿色方块的浓度为60%,我们就把红色的浓度设为40%(1.0 – 0.6)。所以我们把FdestinationFdestination设置为1减去源颜色向量的alpha值。方程将变成:

 最终方块结合部分包含了60%的绿色和40%的红色,得到一种脏兮兮的颜色:

最后的颜色被储存到颜色缓冲中,取代先前的颜色。

这个方案不错,但我们怎样告诉OpenGL来使用这样的因子呢?恰好有一个叫做glBlendFunc的函数。

3、glBlendFunc函数

源因子和目标因子是可以通过glBlendFunc函数来进行设置的。

void glBlendFunc(GLenum sfactor, GLenum dfactor)有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。

注意,颜色常数向量C¯constant,C¯constant可以用glBlendColor函数分开来设置。

GL_ZERO:      表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE:       表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。

除此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha值和RGB值采用不同的混合因子。但这些都不是我们现在所需要了解的。毕竟这还是入门教材,不需要整得太复杂。

举例如下:

  • glBlendFunc(GL_ONE, GL_ZERO);则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
  • glBlendFunc(GL_ZERO, GL_ONE);则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
  • glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);则表示源颜色乘以自身的alpha值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
  • glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。

也可以为RGB和alpha通道各自设置不同的选项,使用glBlendFuncSeperate

glBlendFuncSeperate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,GL_ONE, GL_ZERO);

这个方程就像我们之前设置的那样,设置了RGB元素,但是只让最终的alpha元素被源alpha值影响到。

OpenGL给了我们更多的自由,我们可以改变方程源和目标部分的操作符。现在,源和目标元素已经相加了。如果我们愿意的话,我们还可以把它们相减。

void glBlendEquation(GLenum mode)允许我们设置这个操作,有3种可行的选项:

通常我们可以简单地省略glBlendEquation因为GL_FUNC_ADD在大多数时候就是我们想要的,但是如果你如果你真想尝试努力打破主流常规,其他的方程或许符合你的要求。

四、渲染半透明纹理

现在我们知道OpenGL如何处理混合,是时候把我们的知识运用起来了,我们来添加几个半透明窗子。我们会使用本教程开始时用的那个场景,但是不再渲染草纹理,取而代之的是来自教程开始处半透明窗子纹理。

首先,初始化时我们需要开启混合,设置合适的混合方程:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

由于我们开启了混合,就不需要丢弃片段了,所以我们把片段着色器设置为原来的那个版本:

#version 330 core
in vec2 TexCoords;
out vec4 color;
uniform sampler2D texture1;
void main()
{
    color = texture(texture1, TexCoords);
}

这一次(无论OpenGL什么时候去渲染一个片段),它都根据alpha值,把当前片段的颜色和颜色缓冲中的颜色进行混合。因为窗子的玻璃部分的纹理是半透明的,我们应该可以透过玻璃看到整个场景。

如果你仔细看看,就会注意到有些不对劲。前面的窗子透明部分阻塞了后面的。为什么会这样?

原因是深度测试在与混合的一同工作时出现了点状况。当写入深度缓冲的时候,深度测试不关心片段是否有透明度,所以透明部分被写入深度缓冲,就和其他值没什么区别。结果是整个四边形的窗子被检查时都忽视了透明度。即便透明部分应该显示出后面的窗子,深度缓冲还是丢弃了它们。

所以我们不能简简单单地去渲染窗子,我们期待着深度缓冲为我们解决这所有问题;这也正是混合之处代码不怎么好看的原因。为保证前面窗子显示了它后面的窗子,我们必须首先绘制后面的窗子。这意味着我们必须手工调整窗子的顺序,从远到近地逐个渲染。

对于全透明物体,比如草叶,我们选择简单的丢弃透明像素而不是混合,这样就减少了令我们头疼的问题(没有深度测试问题)。

五、三维空间的混合功能

1、实现三维混合

在进行三维场景的混合时必须注意的,那就是深度缓冲
深度缓冲是这样一段数据,它记录了每一个像素距离观察者有多近。在启用深度缓冲测试的情况下,如果将要绘制的像素比原来的像素更近,则像素将被绘制。否则,像素就会被忽略掉,不进行绘制。这在绘制不透明的物体时非常有用——不管是先绘制近的物体再绘制远的物体,还是先绘制远的物体再绘制近的物体,或者干脆以混乱的顺序进行绘制,最后的显示结果总是近的物体遮住远的物体。
然而在你需要实现半透明效果时,发现一切都不是那么美好了。如果你绘制了一个近距离的半透明物体,则它在深度缓冲区内保留了一些信息,使得远处的物体将无法再被绘制出来。虽然半透明的物体仍然半透明,但透过它看到的却不是正确的内容了。
要解决以上问题,需要在绘制半透明物体时将深度缓冲区设置为只读,这样一来,虽然半透明物体被绘制上去了,深度缓冲区还保持在原来的状态。如果再有一个物体出现在半透明物体之后,在不透明物体之前,则它也可以被绘制(因为此时深度缓冲区中记录的是那个不透明物体的深度)。以后再要绘制不透明物体时,只需要再将深度缓冲区设置为可读可写的形式即可

怎么绘制一个一部分半透明一部分不透明的物体?只需要把物体分为两个部分,一部分全是半透明的,一部分全是不透明的,分别绘制就可以了。
即使使用了以上技巧,仍然不能随心所欲的按照混乱顺序来进行绘制。必须是先绘制不透明的物体,然后绘制透明的物体。否则,假设背景为蓝色,近处一块红色玻璃,中间一个绿色物体。如果先绘制红色半透明玻璃的话,它先和蓝色背景进行混合,则以后绘制中间的绿色物体时,想单独与红色玻璃混合已经不能实现了。
总结起来,绘制顺序就是:首先绘制所有不透明的物体。如果两个物体都是不透明的,则谁先谁后都没有关系。然后,将深度缓冲区设置为只读。接下来,绘制所有半透明的物体。如果两个物体都是半透明的,则谁先谁后只需要根据自己的意愿(注意了,先绘制的将成为“目标颜色”,后绘制的将成为“源颜色”,所以绘制的顺序将会对结果造成一些影响)。最后,将深度缓冲区设置为可读可写形式。
调用glDepthMask(GL_FALSE);可将深度缓冲区设置为只读形式。

调用glDepthMask(GL_TRUE);可将深度缓冲区设置为可读可写形式。

2、别打乱顺序

要让混合在多物体上有效,我们必须先绘制最远的物体,最后绘制最近的物体。普通的无混合物体仍然可以使用深度缓冲正常绘制,所以不必给它们排序。我们一定要保证它们在透明物体前绘制好。当无透明度物体和透明物体一起绘制的时候,通常要遵循以下原则:

先绘制所有不透明物体。 为所有透明物体排序。 按顺序绘制透明物体。 一种排序透明物体的方式是,获取一个物体到观察者透视图的距离。这可以通过获取摄像机的位置向量和物体的位置向量来得到。接着我们就可以把它和相应的位置向量一起储存到一个map数据结构(STL库)中。map会自动基于它的键排序它的值,所以当我们把它们的距离作为键添加到所有位置中后,它们就自动按照距离值排序了:

std::map<float, glm::vec3> sorted;
for (GLuint i = 0; i < windows.size(); i++) // windows contains all window positions
{
    GLfloat distance = glm::length(camera.Position - windows[i]);
    sorted[distance] = windows[i];
}

最后产生了一个容器对象,基于它们距离从低到高储存了每个窗子的位置。

随后当渲染的时候,我们逆序获取到每个map的值(从远到近),然后以正确的绘制相应的窗子:

for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)
{
    model = glm::mat4();
    model = glm::translate(model, it->second);
    glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));
    glDrawArrays(GL_TRIANGLES, 0, 6);
}

我们从map得来一个逆序的迭代器,迭代出每个逆序的条目,然后把每个窗子的四边形平移到相应的位置。这个相对简单的方法对透明物体进行了排序,修正了前面的问题,现在场景看起来像这样:

虽然这个按照它们的距离对物体进行排序的方法在这个特定的场景中能够良好工作,但它不能进行旋转、缩放或者进行其他的变换,奇怪形状的物体需要一种不同的方式,而不能简单的使用位置向量。

在场景中排序物体是个有难度的技术,它很大程度上取决于你场景的类型,更不必说会耗费额外的处理能力了。完美地渲染带有透明和不透明的物体的场景并不那么容易。有更高级的技术例如次序无关透明度(order independent transparency),但是这超出了本教程的范围。现在你不得不采用普通的混合你的物体,但是如果你小心谨慎,并知道这个局限,你仍可以得到颇为合适的混合实现。

六、总结

混合就是在绘制时,不是直接把新的颜色覆盖在原来旧的颜色上,而是将新的颜色与旧的颜色经过一定的运算,从而产生新的颜色。新的颜色称为源颜色,原来旧的颜色称为目标颜色。传统意义上的混合,是将源颜色乘以源因子,目标颜色乘以目标因子,然后相加。

源因子和目标因子是可以设置的。源因子和目标因子设置的不同直接导致混合结果的不同。将源颜色的alpha值作为源因子,用1.0减去源颜色alpha值作为目标因子,是一种常用的方式。这时候,源颜色的alpha值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。
在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。
在进行三维混合时,不仅要考虑源因子和目标因子,还应该考虑深度缓冲区。必须先绘制所有不透明的物体,再绘制半透明的物体。在绘制半透明物体时前,还需要将深度缓冲区设置为只读形式,否则可能出现画面错误。

七、参考代码

混合功能演示demo下载链接:

https://github.com/androidcode2013/Demo/tree/master/BlendDemo

公共库下载链接:

https://github.com/androidcode2013/Demo/tree/master/openglLibrary

参考文章

https://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/03%20Blending/

OpenGL———混合的基本知识 - Anita-ff - 博客园

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java线程池的几个重要核心参数

一、corePoolSize&#xff08;核心线程数&#xff09; 含义&#xff1a;线程池中始终保持存活的线程数量。作用&#xff1a;当有新任务提交时&#xff0c;如果线程池中线程数量小于核心线程数&#xff0c;会创建新线程来执行任务。即使这些线程处于空闲状态&#xff0c;它们也…

03 django管理系统 - 部门管理 - 部门列表

部门管理 首先我们需要在models里定义Dept类 # 创建部门表 class Dept(models.Model):name models.CharField(max_length100)head models.CharField(max_length100)phone models.CharField(max_length15)email models.EmailField()address models.CharField(max_length2…

【数据采集工具】Flume从入门到面试学习总结

国科大学习生活&#xff08;期末复习资料、课程大作业解析、大厂实习经验心得等&#xff09;: 文章专栏&#xff08;点击跳转&#xff09; 大数据开发学习文档&#xff08;分布式文件系统的实现&#xff0c;大数据生态圈学习文档等&#xff09;: 文章专栏&#xff08;点击跳转&…

进入 Searing-66 火焰星球:第一周游戏指南

Alpha 第四季已开启&#xff0c;穿越火焰星球 Searing-66&#xff0c;带你开启火热征程。准备好勇闯炙热的沙漠&#xff0c;那里有无情的高温和无情的挑战在等待着你。从高风险的烹饪对决到炙热的冒险&#xff0c;Searing-66 将把你的耐力推向极限。带上充足的水&#xff0c;天…

【热门】软件管理系统erp,研+产+供+销+业+财+数据一体

随着科技的进步,原有农业种植方式已经不能满足社会发展的需要,必须对传统的农业进行技术更新和改造。经过多年的实践,人们总结出一种新的种植方法——温室农业,即“用人工设施控制环境因素,使作物获得最适宜的生长条件,从而延长生产季节,获得最佳的产出”。这种农业生产方式…

【mod分享】极品飞车10卡本峽谷白日mod,在白天竞速也是一种很棒的体验,更多的车辆,更高清的材质,更棒的灯光效果、同样光追

各位好&#xff0c;今天小编给大家带来一款新的高清重置魔改MOD&#xff0c;本次高清重置的游戏叫《极品飞车10卡本峡谷》。 《极品飞车10&#xff1a;卡本峡谷》继承了前几款游戏的开放式环境的特点&#xff0c;并且在此基础上做出了很大的改进。这次玩家仍旧要开着车在城市里…

游戏逆向基础-找释放技能CALL

思路&#xff1a;通过send断点然后对send的data参数下写入断点找到游戏里面的技能或者攻击call 进入游戏先选好一个怪物&#xff08;之所以要先选好是因为选怪也会断&#xff0c;如果直接左键打怪的话就会断几次&#xff09; 断下来后对参数下硬件写入断点 硬件断点断下来后先…

ubuntu下安装mysql遇到的问题

ubuntu下安装mysql sudo apt install -y mysql-server 出现问题 ……by process 3455 解决 安装 启动 systemctl status mysql.service sudo mysql -u root -p 如何修改密码 与datagrip的连接 查看IP ifconfig 若没安装 参考 Windows10的DataGrip2024.1.4连接ubuntu22.04中的M…

前端布局与响应式设计综合指南(三)

​&#x1f308;个人主页&#xff1a;前端青山 &#x1f525;系列专栏&#xff1a;Css篇 &#x1f516;人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Css篇专栏内容:前端布局与响应式设计综合指南(三) 目录 42、px/em/rem有什么区别&#xff1f;为什么通常给font-s…

23种设计模式之工厂方法模式

文章目录 1. 简介2. 代码2.1 抽象类&#xff1a;Course.java2.2 产品A&#xff1a;JavaCourse.java2.3 产品B&#xff1a;PythonCourse.java2.4 工厂抽象类&#xff1a;CourseFactory.java2.5 产品A的工厂A&#xff1a;JavaCourseFactory.java2.6 产品B的工厂B&#xff1a;PyCo…

Java实现文件上传功能

目录 1、准备工作 2、注意事项 3、jsp页面代码 4、Servlet 5、注册Servlet 1、准备工作 导入依赖&#xff1a;commons-fileupload和commons-io 2、注意事项 ①为保证服务器安全&#xff0c;上传文件应该放在外界无法直接访问的目录下&#xff0c;比如WEB-INF目录下 ②为…

力扣66~70题

题66&#xff08;简单&#xff09;&#xff1a; python代码&#xff1a; class Solution:def plusOne(self, digits: List[int]) -> List[int]:s_str.join([str(i) for i in digits])nstr(int(s_str)1)n_strlist(n)res[int(i) for i in n_str]return res题67&#xff08;简…

Java项目-基于Springboot的智慧养老平台项目(源码+文档).zip

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、SpringClud、Vue、Mybaits Plus、ELementUI工具&…

ASP.NET Core8.0学习笔记(二十二)——单向导航属性

一、单向导航属性引入 1.双向导航属性存在的问题&#xff1a;数据库中存在一些“基础表”&#xff0c;这些表会被其他各种表来引用。比如有一张User表&#xff0c;另有请假表&#xff08;请假人、审批人&#xff09;、采购表&#xff08;采购员、审核员&#xff09;等多个表的…

[PHP]__callStatic

第一种&#xff1a;以下代码不会触发__callStatic&#xff0c;也不会报错 test是空方法 <?php class A {public function test(){}public static function __callStatic($method, $args){print_r(aaaaaaaaaaaaaaaaaaaaa);} }A::test();第二种&#xff1a;以下代码不会触发…

【在Linux世界中追寻伟大的One Piece】应用层自定义协议|序列化

目录 1 -> 应用层 2 -> 网络版计算器 3 -> 序列化与反序列化 4 -> 重新理解read、write、recv、send和tcp为什么支持全双工 5 -> 开始实现 5.1 -> 定制协议 5.2 -> 关于流式数据的处理 1 -> 应用层 应用层是OSI模型或TCP/IP模型中的最高层&…

【D3.js in Action 3 精译_035】4.1 D3 中的坐标轴的创建(下篇):坐标轴与轴标签的具体实现

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一部分 D3.js 基础知识 第一章 D3.js 简介&#xff08;已完结&#xff09; 1.1 何为 D3.js&#xff1f;1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践&#xff08;上&#xff09;1.3 数据可…

【Vue.js设计与实现】第三篇第9章:渲染器-简单Diff算法-阅读笔记

文章目录 9.1 减少 DOM 操作的性能开销9.2 DOM 复用与 key 的作用9.3 找到需要移动的元素9.4 如何移动元素9.5 添加新元素9.6 移除不存在的元素 系列目录&#xff1a;【Vue.js设计与实现】阅读笔记目录 当新旧vnode 的子节点都是一组节点时&#xff0c;为了以最小的性能…

《深度学习》OpenCV库、Dlib库 人脸检测 案例解析

目录 一、Dlib库 1、什么是Dlib库 2、OpenCV优缺点 1&#xff09;优点 2&#xff09;缺点 3、Dlib库优缺点 1&#xff09;优点 2&#xff09;缺点 4、安装Dlib库 二、案例实现 1、对图片进行人脸识别 运行结果&#xff1a; 2、使用摄像头或对视频检测人脸 运行结…

安装和简单使用Milvus

安装和简单使用Milvus 1 介绍 Milvus是国产的高性能分布式向量数据库。 # Milvus官网 https://milvus.io/# 安装文档 https://milvus.io/docs/install-overview.md# Python的对应关系和接口文档 https://milvus.io/api-reference/pymilvus/v2.4.x/About.md2 安装Milvus 2.1…