使用CCRenderTexture来创建动态纹理

基于Simulink的前馈解耦SVPWM整流器仿真 本文提出了一种基于Simulink的前馈解耦SVPWM整流器仿真方案,针对传统双闭环控制存在的耦合严重、电压利用率低等问题进行优化。通过前馈补偿dq轴耦合项(ωLiq和ωLid)结合SVPWM调制算法,实现了电流跟踪误差<3%、电压利用率达0.99、THD<3%的性能指标。文章详细阐述了系统架构设计,包括前馈解耦原理推导、SVPWM算法实现步骤,并提供了完整的Simulink建模方法。仿真结果表明,该方案在动态响应速度(25ms)和直流电压纹波(1.1%)方面显著优于传统SPWM控制。同时给出了 阅读详情
Ray Wenderlich

这篇文章还可以在这里找到 英语

Create textures on the fly (such as these stripes) with CCRenderTexture!

如何使用CCRenderTexture来创建动态纹理

对于事先制作好的背景图片,我想你已经非常熟知如何把它们添加进游戏里面了。但是,你有没有想过动态地创建背景图片并且修改它们的颜色,梯度(gradients)及实时改变效果呢?
如果你玩过appstore上面的 Tiny Wings by Andreas Illiger , 实际上你已经体验过上面提及的想法了。

在这个系列的教程中,你将会学习到所有这些技术要点:

  • 如何用代码实时动态创建纹理
  • 如何使用Gimp创建无缝拼接的纹理
  • 如何混合阴影和光照来增强现实效果
  • 如何创建带状纹理
  • 如何制作重复纹理
  • 还有更多!

这个教程是基于 Sergey Tikhonov 在cocos2d论坛里面讨论 investigation of Tiny Wings 时所制作的一个样例工程来制作的。

 Sergey在demo工程上面做了一件非常好的工作,因此,为了不重复发明轮子,我将把他的代码转换成一个系列的教程,同时还添加了一些很酷的特性。
这个教程假设你对于cocos2d已经非常熟悉了。如果你对cocos2d还很陌生的话,建议你先看看我博客上面的其它 Cocos2D 教程 on this site.

使用CCRenderTexture来动态创建纹理

OTiny Wings有一个很酷的特性就是,每一天都会改变纹理,就如同下面的游戏截屏一下:

Tiny Wings Dynamic Backgrounds

但是,你怎么在cocos2d里面来动态创建纹理呢?好吧,其实有一个很酷的类,叫CCRenderTexture,它允许你来动态创建纹理,并且可以在游戏中重用这些纹理。

使用 CCRenderTexture非常简单 – 你只需要做以下5步就行了:

  1. 创建一个新的CCRenderTexture. 这里,你可以指定将要创建的纹理的宽度和高度。.
  2. 调用 CCRenderTexture:begin. 这个方法会启动OpenGL,并且接下来,任何绘图的命令都会渲染到CCRenderTexture里面去,而不是画到屏幕上。.
  3. 绘制纹理. 你可以使用原始的OpenGL调用来绘图,或者你也可以使用cocos2d对象里面已经定义好的visit方法。(这个visit方法就会调用一些opengl命令来绘制cocos2d对象)
  4. 调用 CCRenderTexture:end. 这个方法会渲染纹理,并且会关闭渲染至CCRenderTexture的通道。.
  5. 从生成的纹理中创建一个sprite.你现在可以用CCRenderTexture的sprite.texture属性来轻松创建新的精灵了。

Note that you can repeat steps 1-3 to continually add/modify the texture over time. For example this might be handy to implement a drawing app. However for this tutorial, we just need to do the drawing once and then we’re done.
注意你可以重复一到三部来持续的增加或修改纹理。比如这可以很方便的实现一个绘图应用程序。然后在这篇教程中,我们只需绘画一次。

让我们来实践一下具体这个过程是如何工作的,我们先来创建一个简单的单色的纹理。

但是,首先,你需要一个新的工程!因此,打开Xcode,点击FileNewNew Project,然后选择 iOScocos2dcocos2d_box2d template。尽管这个教程并没有使用到box2d,但是,我们后续的教程会用到,所以,我在这里就提前先建立好。把工程命名为TinySeal,点击Next,然后选择磁盘上的一个文件夹并点击Create。

然后,打开HelloWorldLayer.h,并用以下内容替换之:

#import "cocos2d.h"
 
@interface HelloWorldLayer : CCLayer
{
	CCSprite * _background;
}
 
+(CCScene *) scene;
 
@end

这里移除掉了”Hello,World“box2d的代码,并且往里面添加了一个实例变量来追踪我们将要动态创建的背景。
接下来,打开HelloWorldLayer.m,并把它替换成下面的样子:(只需要把box2d相关代码全部删除并且得到一个空的场景就可以了)  

#import "HelloWorldLayer.h"
 
@implementation HelloWorldLayer
 
+(CCScene *) scene {
	CCScene *scene = [CCScene node];
	HelloWorldLayer *layer = [HelloWorldLayer node];
	[scene addChild: layer];
	return scene;
}
 
-(id) init {
	if((self=[super init])) {   
	}
	return self;
}
@end

你现在可以编译并运行一下,你将会看到一个黑色的屏幕。
接下来,在init方法的上面添加下面的新方法:

-(CCSprite *)spriteWithColor:(ccColor4F)bgColor textureSize:(float)textureSize {
 
    // 1: Create new CCRenderTexture
    CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureSize height:textureSize];
 
    // 2: Call CCRenderTexture:begin
    [rt beginWithClear:bgColor.r g:bgColor.g b:bgColor.b a:bgColor.a];
 
    // 3: Draw into the texture
    // We'll add this later
 
    // 4: Call CCRenderTexture:end
    [rt end];
 
    // 5: Create a new Sprite from the texture
    return [CCSprite spriteWithTexture:rt.sprite.texture];
 
}

你可以看到,这里动态创建纹理所采用的五个步骤和我们之前讨论的一模一样。
注意,我们这里不是调用的CCRenderTexture:begin方法,而是调用另外一个较方便的方法beginWithClear:g:b:a:,这个方法可以用给定的颜色来清除纹理的背景,相当于设置画布的颜色。
我们还没有在画布上画任何东西–目前为止,我们仅仅是看看纯色纹理看起来是什么样子.
把上面所讨论的一起打包起来,并且添加一些新的方法,然后把init方法改成下面的样子:

- (ccColor4F)randomBrightColor {
 
    while (true) {
        float requiredBrightness = 192;
        ccColor4B randomColor = 
            ccc4(arc4random() % 255,
                 arc4random() % 255, 
                 arc4random() % 255, 
                 255);
        if (randomColor.r > requiredBrightness || 
            randomColor.g > requiredBrightness ||
            randomColor.b > requiredBrightness) {
            return ccc4FFromccc4B(randomColor);
        }        
    }
 
}
 
- (void)genBackground {
 
    [_background removeFromParentAndCleanup:YES];
 
    ccColor4F bgColor = [self randomBrightColor];
    _background = [self spriteWithColor:bgColor textureSize:512];
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
    _background.position = ccp(winSize.width/2, winSize.height/2);        
    [self addChild:_background z:-1];
 
}
 
-(id) init {
    if((self=[super init])) {		        
        [self genBackground];
        self.isTouchEnabled = YES;        
    }
    return self;
}
 
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
 
    [self genBackground];
 
}

randomBrightColor方法是一个辅助方法,用来创建一种随机颜色。注意,这里使用ccc4B(因此,我们能够在0-255的范围内指定R/G/B/A值),同时确保至少有一个颜色分量是大于192的,这样的话,我们就不会得到较暗的颜色。
  然后,genBackground调用我们之前写的spriteWithColor方法,同时把它加屏幕中央。
  至于init函数,它调用genBackground方法,同时激活touches事件,这样的话,你就可以通过点击屏幕来获得另外的随机背景了。
  编译并运行,这样你每一次点击屏幕,你都可以得到一张不同的单色背景图片啦!

A dynamic background with a random color

往纹理里面加点噪声

在Tiny Wings里面,你可能已经注意到了,那纹理并不只是纯粹的单色—而是被一些噪声所修饰了一下,这样,看起来的话就会有阴影和光照。

你可以用代码来动态地生成了一些噪声,但是,如果使用一些预先制作的噪声的话,那样速度会更快一些–这也是我接下来将要在教程里所使用的方法:

一种比较简单的方法,就是使用一个免费的图片处理程序,叫做 Gimp. 接下来这一部分将向您展示如何动手制作这样一个噪声纹理,如果你不乐意自己动手制作那张蒙板,可以用我提供的这张。

当然,你也可以下载我已经做好了的 噪声图片并且把它添加到工程里面去,同时可以跳过接下来的部分了:)

如果你想继续,那么先下载Gimp,并且安装好。
安装好之后,打开Gimp,选择FileNew,然后创建一张新的512*512的图片。然后选择 FilterRenderCloudsSolidNoise,并且可以调节一些参数来看看效果,调好之后就点击Ok吧。
  你可以会得到和下图差不多的图片:

Creating random noise with Gimp

我们将使用这张图片去乘以纹理的颜色。因此,噪声图片中白色的部分,纹理中的颜色就会被显示出来,而噪声图片中黑色的地方,纹理图片对应部位的颜色就会被变暗。
上图中可以看出,黑色部分占太多了,这样会影响我们的原图效果。因此,我们要把黑色的部位去掉一些,选择 ColorsLevels,然后”Output Levels“的最左边的滑动条,把它拖到右边去一点,这样我们的噪声图片就会明亮一些啦。

Modifying levels with Gimp

当你做完之后就点击ok,然后,还有最后一步,这个噪声图片纹理要制作成无缝拼接的,因此,这样我们才能制作无限滚动的背景。
Gimp使用得这个功能变得非常简单。只需要点击FiltersMapMake Seamless就行了!
使用FileSave As来保存你的图片为Noise.png。然后使用Finder打开并拖到TinySeals工程中去。确保“Copy items into destination group’s folder”被复选中,然后点击Finish。
恭喜你,现在你有一张噪声纹理了,位下来,你可以使用它来创建更加真实的纹理效果了!
现在,你知道怎么动态创建纹理了,并且你可以使用Gimp的滤镜来制作一些不同的效果!

往纹理中添加噪声

现在,你有一张带噪声的图片了,接下来,让我们把它添加到CCRenderTexture里去吧。

在spriteWithColor:textureSize方法里面,我们在注释中所示的第3步,添加下列代码:

CCSprite *noise = [CCSprite spriteWithFile:@"Noise.png"];
[noise setBlendFunc:(ccBlendFunc){GL_DST_COLOR, GL_ZERO}];
noise.position = ccp(textureSize/2, textureSize/2);
[noise visit];

这里使用噪声纹理来创建一个精灵,然后把它放置在render texture的中心位置,并且调用visit方法。这个visit方法就是一系列的opengl调用,且这些调用是用来画这个纹理的。
  这里只有一个诡异的地方—-就是”setBlendFunc“方法。这玩意儿倒底干吗的呢?
  好吧,这第一个常量(GL_DST_COLOR)指定如何乘以输入图像/源图像的颜色数据(也就是指噪声图像),第二个参数(GL_ZERO)指定如何乘以已经存在的图像/目标图像颜色数据(实际上就是之前的单色纹理)。
  因此,阐述一下:

  • 已经存在的颜色乘以GL_ZERO,那意味着已经存在的颜色将会被清除掉。
  • 噪声纹理颜色乘以GL_DST_COLOR。而GL_DST_COLOR意味着已经存在的颜色,因此噪声颜色会乘以已经存在的颜色。所以,如果噪声中白色越多,那么对应位置的源图像位置显示的颜色也就越亮。如果噪声颜色中越多黑色,那么源图像的颜色就会变得越暗。
  • 上面提及的两种颜色叠加到一起,但是,由于指点一个参数是0,所以,实际上还是看第二个参数的影响。

顺便提一下,我发现这些混合常量非常容易使人迷惑,但是,我们很幸运,这里有 一款免费的在线工具 可以用来动态地配置每种常量的效果。

 不管怎么说,这就是你所需要的全部东西啦!编译并运行你的代码,现在,你可以看到纹理的一些阴影效果了。注意,这个在模拟器上看起来效果通常不是很好,你可能需要在实际的设备上尝试一下会比较好。

A dynamic background with noise applied for shadows and lighting

往纹理中添加梯度

为了使纹理看起来更好看,让我们从上至下添加一些梯度吧,具体的效果是纹理从上到下,会变得越来越暗。
  我们也可以使用Gimp来修改我们的噪声图片,但是,这一次,我们可以在代码中来完成,这样会变得更加动态,并且可以很容易地修改。
  基本的想法就是,我们在纹理的顶部画一个黑色的矩形,但是,它将会是上部完全透明的,并且底部完全不透明。这样会使得上面比较亮,而慢慢得往下变暗。
  为了实现这个目标,我们需要使用一些opengl命令。如果你对opengl不熟悉的话,也不用担心—我会帮你写好这些代码,并且在高层向你解释具体是如何工作的。在教程的结尾,你会得到一些参考资料,通过它们你可以获得更多的信息。
  在spriteWithColor:textureSize里面,在创建噪声精灵之前,添加下列代码:

glDisable(GL_TEXTURE_2D);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 
float gradientAlpha = 0.7;    
CGPoint vertices[4];
ccColor4F colors[4];
int nVertices = 0;
 
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0 };
vertices[nVertices] = CGPointMake(textureSize, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(0, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
vertices[nVertices] = CGPointMake(textureSize, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
 
glVertexPointer(2, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
 
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);

 第一件事情就是禁用GL_TEXTURE_2D 和 GL_TEXTURE_COORD_ARRA两个opengl状态,因为,我们接下来只需要绘制颜色—这与纹理无关。
  一个比较奇怪的事就是,这里绘制纹理的时候,左上角是坐标原点(0,0)—而不是我们在cocos2d里面使用的,0,0点在左下角。
  因此,接下来,我们定义纹理的四个顶点坐标左上、右上、左下、右下–并且同时指定每一个点的颜色值。
  你可能会奇怪,为什么顶点会以这样的顺序被定义出来。但是因为,我们将按下图的方式来画两个三角形,当然这两个三角形拼成了一个矩形:

Drawing texture with an OpenGL triangle strip

 我们的将使用GL_TRIANGLE_STRIP来画这些三角形,那意味着,顶点数组中的前三个顶点组成第一个三角形,而第二个三角形就是前面2,3号顶点,再加上下一个新的顶点组成第二个三角形。
  因此,第一个三角形是由顶点v0,v1,v2组成,而第二个则由v1,v2,v3组成。
  在定义完顶点数组和颜色数组以后,我们把它们传给glVertexPointer opengl调用(每一个顶点用2个浮点数标识)和glColorPointer调用(每个颜色值指定4个浮点数),然后调用glDrawArrays,并且指定 GL_TRIANGLE_STRIP为参数来绘制三角形带。
  最后,我们重新激活GL_TEXTURE_COORD_ARRAY 和 GL_TEXTURE_2D两个状态,这样做的话是保持和之前调用的对称,并且不会破坏opengl状态机。
  编译并运行代码,然后你会看到一个整齐的带有梯度的纹理啦!

Dynamic background with noise and gradient

创建带状纹理

 在我们开始写代码来绘制带状纹理之前,让我们先花上一分钟时间来想一想我们的策略。
  我们将从指定纹理为一种颜色开始(如下图的蓝色部分),然后,我们将以对角线的方向画一些三角带子(如下图绿色部分),如图所示:

Algorithm for drawing stripes

注意,因为带子是呈对角线的,我们实际上将要在纹理的边界之外就绘制这条带子。(如上图v0,v1,v2所示)
  还有一点需要注意,为了获得一个比较好的45度的效果,我们将把V0偏移原点texture size的距离。
  好了,让我们来看看实际的代码是什么样子的吧。在init的方法上面添加一个新的方法,如下所示:

-(CCSprite *)stripedSpriteWithColor1:(ccColor4F)c1 color2:(ccColor4F)c2 textureSize:(float)textureSize  stripes:(int)nStripes {
 
    // 1: Create new CCRenderTexture
    CCRenderTexture *rt = [CCRenderTexture renderTextureWithWidth:textureSize height:textureSize];
 
    // 2: Call CCRenderTexture:begin
    [rt beginWithClear:c1.r g:c1.g b:c1.b a:c1.a];
 
    // 3: Draw into the texture    
 
    // Layer 1: Stripes
    glDisable(GL_TEXTURE_2D);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
 
    CGPoint vertices[nStripes*6];
    int nVertices = 0;
    float x1 = -textureSize;
    float x2;
    float y1 = textureSize;
    float y2 = 0;
    float dx = textureSize / nStripes * 2;
    float stripeWidth = dx/2;
    for (int i=0; i<nStripes; i++) {
        x2 = x1 + textureSize;
        vertices[nVertices++] = CGPointMake(x1, y1);
        vertices[nVertices++] = CGPointMake(x1+stripeWidth, y1);
        vertices[nVertices++] = CGPointMake(x2, y2);
        vertices[nVertices++] = vertices[nVertices-2];
        vertices[nVertices++] = vertices[nVertices-2];
        vertices[nVertices++] = CGPointMake(x2+stripeWidth, y2);
        x1 += dx;
    }
 
    glColor4f(c2.r, c2.g, c2.b, c2.a);
    glVertexPointer(2, GL_FLOAT, 0, vertices);
    glDrawArrays(GL_TRIANGLES, 0, (GLsizei)nVertices);
 
    glEnableClientState(GL_COLOR_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
    glEnable(GL_TEXTURE_2D);
 
    // Layer 2: Noise    
    CCSprite *noise = [CCSprite spriteWithFile:@"Noise.png"];
    [noise setBlendFunc:(ccBlendFunc){GL_DST_COLOR, GL_ZERO}];
    noise.position = ccp(textureSize/2, textureSize/2);
    [noise visit];
 
    // 4: Call CCRenderTexture:end
    [rt end];
 
    // 5: Create a new Sprite from the texture
    return [CCSprite spriteWithTexture:rt.sprite.texture];
 
}

这个方法的大部分内容都是重温如何创建一个CCRenderTexture,只有创建带状层的代码是新加的。所以让我们来讨论一下。
  它首先创建了一个顶点数组–对于每一条带子,我们需要6个顶点–3个顶点*2个三角形。我们这里不能使用三角形带,因为,带子不是邻接在一起的。
  第一个顶点的坐标是 (-textureSize, textureSize),上面的图示中也可以看出来,接下来一个顶点就是 (-textureSize+stripWidth, textureSize).第三个顶点是(0,0),而第四个顶点是(stripeWidth, textureSize).这些就是我们画一个带子所需要的全部顶点,而且每一次我们往前递增两个带子的长度,并且继续,直至所有的带子都制作完成。
  现在,你可以把genBackground修改成下面的样子了:

- (void)genBackground {
 
    [_background removeFromParentAndCleanup:YES];
 
    ccColor4F bgColor = [self randomBrightColor];
    ccColor4F color2 = [self randomBrightColor];
    //_background = [self spriteWithColor:bgColor textureSize:512];
    int nStripes = ((arc4random() % 4) + 1) * 2;
    _background = [self stripedSpriteWithColor1:bgColor color2:color2 textureSize:512 stripes:nStripes];
 
    self.scale = 0.5;
 
    CGSize winSize = [CCDirector sharedDirector].winSize;
    _background.position = ccp(winSize.width/2, winSize.height/2);        
    [self addChild:_background];
 
}

这里调用新的方法,同时修改层的scale为0.5,这样的话就能看到512*512的纹理的全貌。
  编译并运行,现在,你每点击一下屏幕都可以得到一张不同的带状的纹理了!

Dynamically generated stripe

重复背景

对于带状背景和梯度背景,我们都希望它比实际定义的512*512的区域要大。
  一种比较简单的方式就是我们创建多个CCSprites,然后把它们链接在一起。但是,那看起来太疯狂了,因为,我们有更加简单的方法—我们可以设置纹理并让它们重复。
  打开HelloWorldLayer.m,并作如下修改:

// Add to genBackground, right BEFORE the call to addChild
ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
[_background.texture setTexParameters:&tp];
 
// Add to bottom of init
[self scheduleUpdate];
 
// Add after init
- (void)update:(ccTime)dt {
 
    float PIXELS_PER_SECOND = 100;
    static float offset = 0;
    offset += PIXELS_PER_SECOND * dt;
 
    CGSize textureSize = _background.textureRect.size;
    [_background setTextureRect:CGRectMake(offset, 0, textureSize.width, textureSize.height)];
 
}

这里最重要的就是纹理参数:

  • GL_LINEAR 是说“当显示纹理时,显示的大小大于或者小于原纹理的尺寸时,使用邻近像素点来插值补点”
  • GL_REPEAT是说“如果你想显示纹理边界之外的像素点时,把它旁边的纹理像素点平铺过去”

 同时,调用一个update定时器,更新纹理的x轴坐标,使前不断地前移。这样会使得纹理看起来随着时间不断地往前移动。
  编译并运行代码,这时你可以看到一个连续滚动的无限重复的背景纹理了!你也可以用带有梯度的纹理来尝试一下,一样可以跑起来。

免费的光照

如果你看一看Tiny Wings的山丘纹理实现,你会发现山的顶部有一些光照,同时从上到下还有一些梯度,并且这一切看起来非常棒!因此, 让我们修改我们前面的带状纹理。
  在stripedSpriteWithColor1方法中,紧接着glDrawArrays之后,添加下面代码:

// layer 2: gradient
glEnableClientState(GL_COLOR_ARRAY);
 
float gradientAlpha = 0.7;    
ccColor4F colors[4];
nVertices = 0;
 
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0 };
vertices[nVertices] = CGPointMake(textureSize, 0);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(0, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
vertices[nVertices] = CGPointMake(textureSize, textureSize);
colors[nVertices++] = (ccColor4F){0, 0, 0, gradientAlpha};
 
glVertexPointer(2, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);
 
// layer 3: top highlight    
float borderWidth = textureSize/16;
float borderAlpha = 0.3f;
nVertices = 0;
 
vertices[nVertices] = CGPointMake(0, 0);
colors[nVertices++] = (ccColor4F){1, 1, 1, borderAlpha};
vertices[nVertices] = CGPointMake(textureSize, 0);
colors[nVertices++] = (ccColor4F){1, 1, 1, borderAlpha};
 
vertices[nVertices] = CGPointMake(0, borderWidth);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
vertices[nVertices] = CGPointMake(textureSize, borderWidth);
colors[nVertices++] = (ccColor4F){0, 0, 0, 0};
 
glVertexPointer(2, GL_FLOAT, 0, vertices);
glColorPointer(4, GL_FLOAT, 0, colors);
glBlendFunc(GL_DST_COLOR, GL_ONE_MINUS_SRC_ALPHA);
glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)nVertices);

这段代码应该又是重新温习一遍啦,但是,第一部分和我们之前创建的梯度背景差不多,而第二部分创建光照的时候,我们从上至下添加,使之有太阳晒着的感觉。
  编译并运行代码,现在,你应该发现我们的带状纹理更加逼真了,有梯度变化又有光照效果!
Finished dynamic background with stripes, highlight, gradient, and shading mask

相关推荐

Ansys Zemax导入CAD文件避坑指南:STL/STEP/IGES格式选择与性能优化

本文深度解析了在Ansys Zemax中导入CAD文件的核心要点与性能优化策略。针对STL、STEP、IGES等主流格式,详细对比了其几何表示、精度与适用场景差异,并提供了基于模型特征的格式选择决策树。文章重点分享了体元、弦公差等高级参数的调优技巧,以及处理复杂装配体和避免精度陷阱的实战指南,旨在帮助光学工程师提升仿真效率与结果可靠性。

weixin_29202687的博客 436

如何使用CCRenderTexture创建动态纹理 cocos2d-x-3.0alpha0

本文实践自 Ray Wenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 3.0进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹理、如何混合阴影和光照到纹理上以显现逼真

杜甲同学的专栏 3592

DAC60501与STM32的I2C通信实战:从寄存器配置到电压输出

本文详细介绍了如何使用STM32通过I2C通信驱动DAC60501数模转换器,实现精确的模拟电压输出。内容涵盖从硬件连接到软件模拟I2C驱动编写,再到DAC寄存器配置与电压计算的全流程实战指南,并提供了关键的驱动实例代码和常见问题调试技巧,帮助开发者快速掌握这一嵌入式开发中的实用技能。

weixin_29197835的博客 367

如何使用CCRenderTexture来创建动态纹理

子龙山人 Learning,Sharing,Improving! (译)如何使用CCRenderTexture来创建动态纹理   免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作! 原文链接地址:http://www.

我用CV我自豪。 913

【Python 实战基础】如何绘制热力图来模拟绘制北京景区热度图

Python如何绘制热力图来模拟绘制北京景区热度图 文件读写 基础语法 字符串处理 文件生成 数据构建

菜鸟实战的博客 3401

Win10网络图标消失,网络图标变成灰色,打开网络设置闪退等问题解决

介绍 主要解决如下问题 网络图标消失 常用方法配置中出现的网络图标变成灰色没有办法打开 打开网络和Internet闪退 错误提示`网络发现已关闭。网络计算机和设备不可见。请启动网络和共享中心的网络发现。 连不上网,诊断结果为 依赖服务或组无法启动 这个问题大概率通过重装系统不能解决,请大家谨慎测试之后最后选择重装这个路线。 方法1:这一步一定要做(卸载三方软件) 把360安全卫士,软件管家这些东西都卸载掉。 我的问题一开始就是这个软件引起的,可能他检测到我还给台式机配了无线网卡,然后就把有线网络的一些

呆呆象呆呆的博客 3万+

20. 如何使用CCRenderTexture来创建动态纹理

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作! 原文链接地址:http://www.raywenderlich.com/3857/how-to-create-dynamic-textures-with-ccrendertexture

Leeyehong_self的专栏 977

STM32 HAL SDADC DMA

由于项目需要使用STM32F373单片机的SDADC功能对电位计进行检测,网上资料比较少,踩了很多坑,下面进行总结。

weixin_44597885的博客 1196

如何使用CCRenderTexture创建动态纹理 Cocos2d-x 2.1.4

本文实践自 Ray Wenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹理、如何混合阴影和光照到纹理上以显现逼真

无幻 1万+

X86/64架构中的总线 和 ARM架构中总线的理解

内部总线 /CPU总线/FSB前端总线:类似ARM架构中的AHB总线北桥? 直接和CPU连接,这条总线主要由CPU使用,用来与高速缓存、主存和北桥(或MCH)之间传送信息。 外部总线:类似ARM架构中的APB总线(外围总线)南桥?例如PCI总线、USB总线等 通过北桥与CPU连接(南桥?),是CPU与内存RAM、ROM和输入/输出设备接口之间进行通讯的通路 内存总线:内存与北桥间的总线称为内存总线 ...

a746742897的博客 1067

实战!C#整合YOLO+Halcon做玻璃瓶口缺陷检测:裂纹识别+尺寸测量一步到位(附DLL交互代码)

AI擅长语义识别(裂纹、缺陷),传统机器视觉擅长精密测量(尺寸、位置),C#做整合层,才能满足工业生产的“精度+效率+稳定性”要求。这套方案不只是玻璃瓶检测能用,改改模型和测量逻辑,还能用到瓶盖缺陷检测、饮料瓶液位检测等场景。如果你们厂也有类似的质检痛点,评论区留个言,我帮你看看怎么调整算法参数。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 870

(译)如何使用CCRenderTexture来创建动态纹理

免责申明(必读!):本博客提供的所有教程的翻译原稿均来自于互联网,仅供学习交流之用,切勿进行商业传播。同时,转载时不要移除本申明。如产生任何纠纷,均与本博客所有人、发表该翻译稿之人无任何关系。谢谢合作! 原文链接地址:http://www.raywenderlich.com/3857/how-to-create-dynamic-textures-with-ccrendertext...

xumingrencai 117

物流分拣YOLO实战:YOLO26 OBB+YOLOv8多尺度融合,不规则包裹分拣效率提升40%(附训练+部署代码)

在物流分拣的工业场景中,模型的检测精度、推理速度、部署友好性本次基于YOLO26 OBB+YOLOv8多尺度融合打造的物流分拣视觉检测方案,精准解决了工业场景中不规则包裹的旋转检测、多尺度包裹的漏检问题,最终实现分拣效率提升40%,检测准确率提升至95%以上,且模型在边缘设备上的推理帧率稳定35FPS+,完全适配物流分拣的工业落地需求。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 882

如何使用CCRenderTexture创建动态纹理

本文实践自 Ray Wenderlich、Ali Hafizji 的文章《How To Create Dynamic Textures with CCRenderTexture in Cocos2D 2.X》,文中使用Cocos2D,我在这里使用Cocos2D-x 2.1.4进行学习和移植。在这篇文章,将会学习到如何创建实时纹理、如何用Gimp创建无缝拼接纹理、如何混合阴影和光照到纹理上以显现逼真

峻峰飞阳的博客 blog.youkuaiyun.com/while0 1523

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

如何使用CCRenderTexture创建动态纹理 Cocos2d-x 2 1 4

如何使用CCRenderTexture创建动态纹理 Cocos2d-x 2 1 4

jhfyuf的博客 185

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: 如何使用NSOperations和NSOperationQueues
下一篇: Storyboard入门到精通:自动布局
Vjsplus
博客等级 码龄13年 1粉丝 1原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值