当前位置: 首页 > news >正文

东莞万江网站建设公司网站推广优化

东莞万江网站建设公司,网站推广优化,网站建设域名未拿到,网站制作好学吗我们在实际用OpenGL等3D图形渲染API时 点图元 往往用得不多,而在粒子系统中可能也是用一个正方形来绘制一单个粒子。不过在当前大部分3D图形渲染API中都能支持用点图元来绘制一个具有纹理贴图的粒子,从早在OpenGL 1.4开始就能支持了,而在Open…

我们在实际用OpenGL等3D图形渲染API时 点图元 往往用得不多,而在粒子系统中可能也是用一个正方形来绘制一单个粒子。不过在当前大部分3D图形渲染API中都能支持用点图元来绘制一个具有纹理贴图的粒子,从早在OpenGL 1.4开始就能支持了,而在OpenGL ES 1.1中,大部分GPU都能实现 GL_OES_point_sprite 这一扩展,同样也能使用此功能。
使用Point Sprite的一大好处就是顶点数量大大降低,本来需要绘制一个具有四个顶点的正方形图元,而现在缩减到了只含一个顶点的点图元,这样大大节省了带宽。此外,GPU对于点精灵的渲染往往也会有特别的优化处理。所以如果我们要制作大规模的粒子特效的话可以考虑使用point sprite技术。

下面我们将分别通过使用固定功能流水线的OpenGL 2.1以及Metal API来讲解如何使用Point Sprite。


OpenGL中使用Point Sprite

在固定功能的OpenGL中使用Point Sprite主要遵循以下几个要点:

  1. 我们需要指定点的大小,可以通过 glPointParameterf 接口通过指定 GL_POINT_SIZE_MINGL_POINT_SIZE_MAX 这两个参数即可。
  2. 我们需要显式使用 glEnable(GL_POINT_SPRITE) 来开启Point Sprite功能。
  3. 在使用粒子效果的纹理时,需要使用 glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE) 对点做纹理元素与像素颜色的插值处理。
  4. 对于固定功能的图形流水线,我们需要将表示粒子效果的纹理单独作为一张图拿出来,而不能合并到其他图上去做采样。另外我们需要确保纹理大小的最小范围。比如,如果当前GPU所能支持的最小纹理图片的分辨率为64x64,那么我们需要提供一张64x64的png图片。

下面我们将列出OpenGL的相关代码。笔者在macOS 10.14系统上通过Xcode 10.1完成的。
首先简单看一下MyGLLayer.h头文件:

//
//  MyGLLayer.h
//  GLPointSprite
//
//  Created by Zenny Chen on 2019/1/24.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//@import Cocoa;
@import QuartzCore;#ifndef let
#define let     __auto_type
#endif@interface MyGLLayer : NSOpenGLLayer@end

然后我们看这里最最关键的MyGLLayer.m源文件:

//
//  MyGLLayer.m
//  GLPointSprite
//
//  Created by Zenny Chen on 2019/1/24.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//#import "MyGLLayer.h"
#include <OpenGL/gl.h>@import OpenGL;@implementation MyGLLayer
{
@private/// 当前OpenGL上下文的像素格式NSOpenGLPixelFormat *mPixelFormat;/// 当前OpenGL的上下文NSOpenGLContext *mContext;/// 纹理IDGLuint mTexName;
}- (instancetype)init
{self = super.init;self.backgroundColor = NSColor.clearColor.CGColor;self.opaque = YES;// 由于我们这里不做周期性动画更新,因此只有当layer接收到setNeedsDisplay消息时才做更新self.asynchronous = NO;NSOpenGLPixelFormatAttribute attrs[] ={// 可选地,我们这里使用了双缓冲机制NSOpenGLPFADoubleBuffer,// 由于我们这里就用固定功能流水线,因此直接是用legacy的OpenGL版本即可NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,// 开启多重采样反走样NSOpenGLPFAMultisample,// 指定一个用于MSAA的缓存NSOpenGLPFASampleBuffers, (NSOpenGLPixelFormatAttribute)1,// 指定MSAA使用四个样本NSOpenGLPFASamples, (NSOpenGLPixelFormatAttribute)4,0};mPixelFormat = [NSOpenGLPixelFormat.alloc initWithAttributes:attrs];mContext = [NSOpenGLContext.alloc initWithFormat:mPixelFormat shareContext:nil];[self.openGLContext makeCurrentContext];// 以垂直刷新率来同步缓存交换[self.openGLContext setValues:(const GLint[]){1} forParameter:NSOpenGLCPSwapInterval];GLfloat fSizes[2];glGetFloatv(GL_ALIASED_POINT_SIZE_RANGE, fSizes);printf("Point minimum size: %.1f, maximum size: %.1f", fSizes[0], fSizes[1]);return self;
}- (void)dealloc
{glDeleteTextures(1, &mTexName);if(mPixelFormat != nil){[mPixelFormat release];mPixelFormat = nil;}if(mContext != nil){[mContext release];mContext = nil;}[super dealloc];
}- (NSOpenGLPixelFormat*)openGLPixelFormat
{return mPixelFormat;
}- (NSOpenGLContext*)openGLContext
{return mContext;
}/// 创建原图像位图数据缓存
/// @param image 指定原图像对象
/// @param pWidth 输出图像宽度
/// @param pHeight 输出图像高度
/// @return 创建出来的图像位图数据
- (uint8_t*)allocSourceImageData:(NSImage*)image width:(int*)pWidth height:(int*)pHeight
{const int width = image.size.width;const int height = image.size.height;if(pWidth != NULL)*pWidth = width;if(pHeight != NULL)*pHeight = height;const size_t length = width * height * 4;uint8_t *buffer = malloc(length);/*** [0] => R* [1] => G* [2] => B* [3] => A*/const CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;// Initialize the source image bufferlet colorSpace = CGColorSpaceCreateDeviceRGB();let context = CGBitmapContextCreate(buffer,width,height,8,         /* bits per component*/width * 4,  /* bytes per row */colorSpace,bitmapInfo);CGColorSpaceRelease(colorSpace);let cImageRef = [image CGImageForProposedRect:NULL context:NULL hints:NULL];CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), cImageRef);CGContextRelease(context);return buffer;
}/// 正常点的顶点数组,左侧为顶点坐标信息,右侧为色值信息
static const GLfloat sNormalVertices[] = {// 左上顶点,红色-0.8f, 0.8f,    0.9f, 0.1f, 0.1f, 1.0f,// 左下顶点,绿色-0.8f, -0.8f,    0.1f, 0.9f, 0.1f, 1.0f,// 右上顶点,蓝色0.8f, 0.8f,    0.1f, 0.1f, 0.9f, 1.0f,// 右下角,黄色0.8f, -0.8f,    0.95f, 0.8f, 0.15f, 1.0f
};/// 具有粒子效果的纹理贴图的点的顶点数组,左侧为顶点坐标,中间为纹理坐标,右侧为色值
static const GLfloat sTexturedVertices[] = {// 左上顶点,红色-0.5f, 0.5f,    0.0f, 0.0f,    0.9f, 0.1f, 0.1f, 1.0f,// 左下顶点,绿色-0.5f, -0.5f,    0.0f, 0.0f,    0.1f, 0.9f, 0.1f, 1.0f,// 右上顶点,蓝色0.5f, 0.5f,    0.0f, 0.0f,    0.1f, 0.1f, 0.9f, 1.0f,// 右下角,黄色0.5f, -0.5f,    0.0f, 0.0f,    0.95f, 0.8f, 0.15f, 1.0f
};- (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp
{const let scale = self.contentsScale;let viewPort = self.frame.size;viewPort.width *= scale;viewPort.height *= scale;// 设置视口大小glViewport(0, 0, viewPort.width, viewPort.height);glEnableClientState(GL_VERTEX_ARRAY);glEnableClientState(GL_COLOR_ARRAY);glVertexPointer(2, GL_FLOAT, 6 * sizeof(GLfloat), sNormalVertices);glColorPointer(4, GL_FLOAT, 6 * sizeof(GLfloat), &sNormalVertices[2]);// 我们这里设置点点大小为32个像素glPointParameterf(GL_POINT_SIZE_MIN, 32.0f);glPointParameterf(GL_POINT_SIZE_MAX, 32.0f);// 设置清除颜色glClearColor(0.4f, 0.5f, 0.4f, 1.0f);// 允许切除面glEnable(GL_CULL_FACE);// 切除背面glCullFace(GL_BACK);// 以逆时针作为正面glFrontFace(GL_CCW);glClear(GL_COLOR_BUFFER_BIT);// 做正交投影变换glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);// 做模型视图变换glMatrixMode(GL_MODELVIEW);glLoadIdentity();glTranslatef(0.0f, 0.0f, -2.3f);// 绘制正常顶点glDrawArrays(GL_POINTS, 0, 4);// 开启颜色混合glEnable(GL_BLEND);// 设置混合方程// 这里设置当前要绘制上的多边形(src)的alpha为ONE,// 因为macOS采用的是pre-multiplied alpha机制,alpha已经与RGBy三个颜色分量相乘了;// 原背景色(dst)的alpha值始终为1.0glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);// 设置纹理if(mTexName == 0){glEnable(GL_TEXTURE_2D);int texWidth, texHeight;let imgBuffer = [self allocSourceImageData:[NSImage imageNamed:@"particle.png"] width:&texWidth height:&texHeight];glGenTextures(1, &mTexName);glBindTexture(GL_TEXTURE_2D, mTexName);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// 开启point spriteglEnable(GL_POINT_SPRITE);// 对纹理环境设置是将纹理与颜色进行混合的关键。// 这里将纹理模式由原来的GL_REPLACE改为GL_COMBINE以对输入颜色做混合,// 当然,这里不设置GL_TEXTURE_ENV这个参数也没有问题。glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE);// 让OpenGL贯穿整个点对纹理坐标进行插值处理glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texWidth, texHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgBuffer);free(imgBuffer);}glEnableClientState(GL_TEXTURE_COORD_ARRAY);glVertexPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), sTexturedVertices);glTexCoordPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), &sTexturedVertices[2]);glColorPointer(4, GL_FLOAT, 8 * sizeof(GLfloat), &sTexturedVertices[4]);// 做正交投影变换glMatrixMode(GL_PROJECTION);glLoadIdentity();glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 3.0f);// 做模型视图变换glMatrixMode(GL_MODELVIEW);glLoadIdentity();glTranslatef(0.0f, 0.0f, -2.3f);// 绘制具有粒子效果的顶点glDrawArrays(GL_POINTS, 0, 4);glFlush();[context flushBuffer];
}@end

下面给出无关紧要的UI相关的ViewController.m的代码:

//
//  ViewController.m
//  GLPointSprite
//
//  Created by Zenny Chen on 2019/1/24.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//#import "ViewController.h"
#import "MyGLLayer.h"@implementation ViewController
{
@private/// MyGLLayer图层对象MyGLLayer *mLayer;
}- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.self.view.wantsLayer = YES;const let viewSize = self.view.frame.size;const let y = viewSize.height - 20.0 - 25.0;CGFloat x = 20.0;let button = [NSButton buttonWithTitle:@"Show" target:self action:@selector(showButtonClicked:)];button.frame = NSMakeRect(x, y, 70.0, 25.0);[self.view addSubview:button];x += button.frame.size.width + 10.0;button = [NSButton buttonWithTitle:@"Close" target:self action:@selector(closeButtonClicked:)];button.frame = NSMakeRect(x, y, 70.0, 25.0);[self.view addSubview:button];
}- (void)showButtonClicked:(NSButton*)sender
{if(mLayer != nil)return;const let viewSize = self.view.frame.size;const let x = (viewSize.width - 512.0) * 0.5;mLayer = MyGLLayer.new;mLayer.contentsScale = NSScreen.mainScreen.backingScaleFactor;mLayer.frame = CGRectMake(x, 50.0, 512.0, 512.0);[self.view.layer addSublayer:mLayer];[mLayer release];
}- (void)closeButtonClicked:(NSButton*)sender
{if(mLayer != nil){[mLayer removeFromSuperlayer];mLayer = nil;}
}- (void)setRepresentedObject:(id)representedObject {[super setRepresentedObject:representedObject];// Update the view, if already loaded.
}@end

最后给出OpenGL绘制的效果:

在这里插入图片描述


Metal API中使用Point Sprite

由于Metal API中实现Point Sprite与可编程流水线的OpenGL十分类似,因此这里就不把可编程流水线的OpenGL实现单独拿出来了。当然,对于OpenGL实现而言,我们仍然需要调用 glEnable(GL_POINT_SPRITE) 来开启Point Sprite功能,并且对具有粒子效果的纹理环境做 glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE) 这种设置。不过我们不是在主机端来指定点的大小了,而是在GPU的顶点着色器端指定。然后在片段着色器中根据当前片段位于点图元的位置做纹理采样,再与颜色值做插值。而在Metal API中不需要对纹理做任何特殊设置。

因此,这里的要点是:

  1. 在Metal API中,我们需要在顶点着色器输出的对象中包含 [[ point_size ]] 属性的成员,指示当前点图元的大小。在OpenGL中则是在顶点着色器中设置 gl_PointSize 内建变量的值即可。它们都是 float 类型。
  2. 在Metal API中,对片段着色器函数显式指定 [[ point_coord ]] 属性的形式参数,它指示在一个点图元内,当前片段所处的位置。其类型为 float2,并且它的x值与y值范围均在[0.0, 1.0]范围内。而在OpenGL中则是直接通过 gl_PointCoord 这一内建变量来访问该位置值。
  3. 因为我们可以在片段着色器中确定片段所处点图元的位置,所以我们可以定位当前片段所对应的纹理坐标。从而,我们不需要将表示粒子效果的纹理单独作为一个图片存放,而是可以将它放到一个大纹理中去采坐标。

下面我们将展示Metal API工程相应的代码。
首先给出MyMetalLayer.h头文件内容:

//
//  MyMetalLayer.h
//  MetalPointSprite
//
//  Created by Zenny Chen on 2019/1/23.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//@import QuartzCore;#ifndef let
#define let     __auto_type
#endif@interface MyMetalLayer : CAMetalLayer/// 设置当前Metal Layer
- (void)setup;@end

然后再给出关键的MyMetalLayer.m源文件:

//
//  MyMetalLayer.m
//  MetalPointSprite
//
//  Created by Zenny Chen on 2019/1/23.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//#import "MyMetalLayer.h"@import Cocoa;
@import Metal;@implementation MyMetalLayer
{
@private/// 命令队列id<MTLCommandQueue> mCommandQueue;/// Metal Shader的库id<MTLLibrary> mLibrary;/// 普通点的顶点缓存id<MTLBuffer> mVertexBuffer;/// 普通点的偏移缓存id<MTLBuffer> mNormalOffsetBuffer;/// 纹理贴图点的顶点缓存id<MTLBuffer> mTexturedVertexBuffer;/// 纹理贴图点的偏移缓存id<MTLBuffer> mTexturedOffsetBuffer;/// 纹理对象id<MTLTexture> mTexture;/// 纹理采样器id<MTLSamplerState> mSamplerState;/// 普通顶点渲染流水线id<MTLRenderPipelineState> mPipelineState;/// 纹理贴图点的渲染流水线id<MTLRenderPipelineState> mTexturedPipelineState;/// 当前所保持的drawableid<CAMetalDrawable> mCurrentDrawable;
}- (instancetype)init
{self = super.init;self.backgroundColor = NSColor.clearColor.CGColor;// 指定该layer为实体,以优化绘制self.opaque = YES;// 使用默认的RGBA8888像素格式self.pixelFormat = MTLPixelFormatBGRA8Unorm;// 默认为YES,但如果我们要在最后渲染的layer上执行计算,那么我们可以将此参数设置为NO。self.framebufferOnly = YES;return self;
}/// 普通的四个点的顶点坐标
static const float sNormalVertices[] = {// 第一个顶点,颜色为红色0.0f, 0.0f,    0.9f, 0.1f, 0.1f, 1.0f,// 第二个顶点,颜色为蓝色0.0f, 0.0f,    0.0f, 0.9f, 0.1f, 1.0f,// 第三个顶点,颜色为绿色0.0f, 0.0f,    0.0f, 0.0f, 0.9f, 1.0f,// 第四个顶点,颜色为白色0.0f, 0.0f,    0.9f, 0.9f, 0.9f, 1.0f
};/// 普通的四个点的偏移位置
static const float sNormalOffsets[] = {// 第一个顶点在左上角-0.8f, 0.8f,// 第二个顶点在左下角-0.8f, -0.8f,// 第三个顶点在右上角0.8f, 0.8f,// 第四个顶点在右下角0.8f, -0.8f
};/// 具有纹理贴图的四个点的顶点坐标
static const float sTexturedVertices[] = {// 第一个顶点,颜色为红色0.0f, 0.0f,    0.0f, 0.59f,    0.9f, 0.1f, 0.1f, 1.0f,// 第二个顶点,颜色为蓝色0.0f, 0.0f,    0.0f, 0.59f,    0.0f, 0.9f, 0.1f, 1.0f,// 第三个顶点,颜色为绿色0.0f, 0.0f,    0.0f, 0.59f,    0.0f, 0.0f, 0.9f, 1.0f,// 第四个顶点,颜色为白色0.0f, 0.0f,    0.0f, 0.59f,    0.9f, 0.9f, 0.9f, 1.0f
};/// 具有纹理贴图的四个点的偏移位置
static const float sTexturedOffsets[] = {// 第一个顶点在左上角-0.5f, 0.5f,// 第二个顶点在左下角-0.5f, -0.5f,// 第三个顶点在右上角0.5f, 0.5f,// 第四个顶点在右下角0.5f, -0.5f
};- (void)dealloc
{[mCommandQueue release];[mLibrary release];[mVertexBuffer release];[mNormalOffsetBuffer release];[mTexturedVertexBuffer release];[mTexturedOffsetBuffer release];[mTexture release];[mSamplerState release];[mPipelineState release];[mTexturedPipelineState release];self.device = nil;[super dealloc];
}- (uint8_t*)allocSourceImageData:(NSImage*)image width:(int*)pWidth height:(int*)pHeight
{const int width = image.size.width;const int height = image.size.height;if(pWidth != NULL)*pWidth = width;if(pHeight != NULL)*pHeight = height;const size_t length = width * height * 4;uint8_t *buffer = malloc(length);/*** [0] => R* [1] => G* [2] => B* [3] => A*/const CGBitmapInfo bitmapInfo = kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big;// Initialize the source image bufferlet colorSpace = CGColorSpaceCreateDeviceRGB();let context = CGBitmapContextCreate(buffer,width,height,8,         /* bits per component*/width * 4,  /* bytes per row */colorSpace,bitmapInfo);CGColorSpaceRelease(colorSpace);let cImageRef = [image CGImageForProposedRect:NULL context:NULL hints:NULL];CGContextDrawImage(context, CGRectMake(0.0f, 0.0f, width, height), cImageRef);CGContextRelease(context);return buffer;
}- (void)setup
{// 1、关联Metal设备let devices = MTLCopyAllDevices();NSLog(@"There are %tu Metal devices available!", devices.count);let device = devices[0];NSLog(@"The current device name: %@", device.name);self.device = device;[devices release];// 2、设置对此layer的绘制区域self.drawableSize = CGSizeMake(self.frame.size.width * self.contentsScale, self.frame.size.height * self.contentsScale);// 3、创建命令队列以及库mCommandQueue = device.newCommandQueue;mLibrary = device.newDefaultLibrary;// 4、分别获取vertex shader、fragment shaderlet vertexProgram = [mLibrary newFunctionWithName:@"point_vertex"];if(vertexProgram == nil){NSLog(@"顶点着色器获取失败");return;}let fragmentProgram = [mLibrary newFunctionWithName:@"point_fragment"];if(fragmentProgram == nil){NSLog(@"片段着色器获取失败");[vertexProgram release];return;}// 5、创建矩形顶点数据缓存mVertexBuffer = [device newBufferWithBytes:sNormalVertices length:sizeof(sNormalVertices) options:MTLResourceCPUCacheModeWriteCombined];mNormalOffsetBuffer = [device newBufferWithBytes:sNormalOffsets length:sizeof(sNormalOffsets) options:MTLResourceCPUCacheModeWriteCombined];// 6、创建流水线状态let descriptor = MTLRenderPipelineDescriptor.new;descriptor.sampleCount = 4;     // 我们将使用多重采样抗锯齿(MSAA),每个像素由4个样本构成descriptor.vertexFunction = vertexProgram;descriptor.fragmentFunction = fragmentProgram;// 像素格式要与CAMetalLayer的像素格式一致descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;descriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;      // 不启用深度测试descriptor.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;    // 不启用stencil[vertexProgram release];[fragmentProgram release];mPipelineState = [device newRenderPipelineStateWithDescriptor:descriptor error:NULL];[descriptor release];// 创建具有纹理贴图的点的顶点缓存mTexturedVertexBuffer = [device newBufferWithBytes:sTexturedVertices length:sizeof(sTexturedVertices) options:MTLResourceCPUCacheModeWriteCombined];mTexturedOffsetBuffer = [device newBufferWithBytes:sTexturedOffsets length:sizeof(sTexturedOffsets) options:MTLResourceCPUCacheModeWriteCombined];// 创建具有纹理贴图的点的顶点、片段程序vertexProgram = [mLibrary newFunctionWithName:@"textured_point_vertex"];if(vertexProgram == nil){NSLog(@"顶点着色器获取失败");return;}fragmentProgram = [mLibrary newFunctionWithName:@"textured_point_fragment"];if(fragmentProgram == nil){NSLog(@"片段着色器获取失败");[vertexProgram release];return;}// 创建纹理贴图点的渲染流水线状态descriptor = MTLRenderPipelineDescriptor.new;descriptor.sampleCount = 4;     // 我们将使用多重采样抗锯齿(MSAA),每个像素由4个样本构成descriptor.vertexFunction = vertexProgram;descriptor.fragmentFunction = fragmentProgram;descriptor.depthAttachmentPixelFormat = MTLPixelFormatInvalid;      // 不启用深度测试descriptor.stencilAttachmentPixelFormat = MTLPixelFormatInvalid;    // 不启用stencil// 像素格式要与CAMetalLayer的像素格式一致descriptor.colorAttachments[0].pixelFormat = MTLPixelFormatBGRA8Unorm;descriptor.colorAttachments[0].blendingEnabled = YES;   // 将飞机渲染流水线设置为允许颜色混合descriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd;descriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd;descriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorOne;descriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorOne;descriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha;descriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha;[vertexProgram release];[fragmentProgram release];mTexturedPipelineState = [device newRenderPipelineStateWithDescriptor:descriptor error:NULL];[descriptor release];// 创建纹理对象let textureDesc = MTLTextureDescriptor.new;textureDesc.textureType = MTLTextureType2D;textureDesc.width = 1024;textureDesc.height = 1024;textureDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;textureDesc.arrayLength = 1;textureDesc.mipmapLevelCount = 1;mTexture = [device newTextureWithDescriptor:textureDesc];[textureDesc release];// 拷贝纹理数据int width = 1024;int height = 1024;let image = [NSImage imageNamed:@"planes_texture.png"];let textureData = [self allocSourceImageData:image width:&width height:&height];[mTexture replaceRegion:MTLRegionMake2D(0, 0, 1024, 1024) mipmapLevel:0 slice:0 withBytes:textureData bytesPerRow:1024 * 4 bytesPerImage:1024 * 1024 * 4];free(textureData);// 创建采样对象let samplerDesc = MTLSamplerDescriptor.new;samplerDesc.minFilter = MTLSamplerMinMagFilterLinear;samplerDesc.magFilter = MTLSamplerMinMagFilterLinear;samplerDesc.sAddressMode = MTLSamplerAddressModeClampToZero;samplerDesc.tAddressMode = MTLSamplerAddressModeClampToZero;samplerDesc.mipFilter = MTLSamplerMipFilterNotMipmapped;samplerDesc.maxAnisotropy = 1;samplerDesc.normalizedCoordinates = YES;samplerDesc.lodMinClamp = 0;samplerDesc.lodMaxClamp = FLT_MAX;mSamplerState = [device newSamplerStateWithDescriptor:samplerDesc];[samplerDesc release];
}/// 获取下一帧的drawble以及下一帧渲染遍描述符
/// @preturn 下一帧的渲染遍描述符
- (MTLRenderPassDescriptor*)nextRenderPass
{// 获取下一帧的drawablelet drawable = self.nextDrawable;// 设置当前DrawablemCurrentDrawable = drawable;let renderPassDesc = MTLRenderPassDescriptor.renderPassDescriptor;// 设置颜色属性let colorAttachment = renderPassDesc.colorAttachments[0];// 每一帧都做清除,以获得最好性能colorAttachment.loadAction = MTLLoadActionClear;colorAttachment.clearColor = MTLClearColorMake(0.4f, 0.5f, 0.4f, 1.0f);colorAttachment.storeAction = MTLStoreActionMultisampleResolve;// 每次都要更新的属性colorAttachment.resolveTexture = drawable.texture;// 设置MSAA纹理属性,像素格式要与CAMetalLayer的像素格式一致let texDesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatBGRA8Unorm width:drawable.texture.width height:drawable.texture.height mipmapped: NO];texDesc.textureType = MTLTextureType2DMultisample;texDesc.resourceOptions = MTLResourceStorageModePrivate;texDesc.sampleCount = 4;texDesc.usage = MTLTextureUsageRenderTarget;let msaaTexture = [self.device newTextureWithDescriptor:texDesc];colorAttachment.texture = msaaTexture;[msaaTexture release];return renderPassDesc;
}- (void)render
{/** 以下为Metal渲染 */if(mCurrentDrawable != nil){NSLog(@"Previous render pass not completed!");return;     // 若之前的命令还没执行完,则直接返回}// 1、创建命令缓存并刷新渲染遍let commandBuffer = mCommandQueue.commandBuffer;[commandBuffer addCompletedHandler:^void(id<MTLCommandBuffer> cmdBuf){// 命令全都执行完之后,将mCurrentDrawable置空,表示可以绘制下面一帧mCurrentDrawable = nil;}];// 2、创建并设置渲染编码器let renderEncoder = [commandBuffer renderCommandEncoderWithDescriptor:self.nextRenderPass];[renderEncoder setRenderPipelineState:mPipelineState];[renderEncoder setVertexBuffer:mVertexBuffer offset:0 atIndex:0];[renderEncoder setVertexBuffer:mNormalOffsetBuffer offset:0 atIndex:1];// 设置面剔除[renderEncoder setCullMode:MTLCullModeBack];// 设置顶点逆时针方向为前面,而默认顺时针方向为前面[renderEncoder setFrontFacingWinding:MTLWindingCounterClockwise];// 3、绘制第一个矩形[renderEncoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:4 instanceCount:1];// 设置纹理贴图点的顶点缓存属性[renderEncoder setRenderPipelineState:mTexturedPipelineState];// 顶点结构体的属性均对应为buffer索引0[renderEncoder setVertexBuffer:mTexturedVertexBuffer offset:0 atIndex:0];[renderEncoder setVertexBuffer:mTexturedOffsetBuffer offset:0 atIndex:1];// 设置片段属性[renderEncoder setFragmentTexture:mTexture atIndex:0];[renderEncoder setFragmentSamplerState:mSamplerState atIndex:0];// 绘制纹理贴图的点[renderEncoder drawPrimitives:MTLPrimitiveTypePoint vertexStart:0 vertexCount:4 instanceCount:1];// 4、结束渲染编码器,并将命令缓存内容呈现到屏幕上[renderEncoder endEncoding];[commandBuffer presentDrawable:mCurrentDrawable];// 5、提交命令[commandBuffer commit];
}- (void)layoutSublayers
{[super layoutSublayers];[self render];
}@end

随后给出这里很重要的Metal shader源文件:

//
//  shaders.metal
//  MetalPointSprite
//
//  Created by Zenny Chen on 2019/1/23.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//#include <metal_stdlib>
using namespace metal;struct ColorInOut
{float4 position [[ position ]];half4  color    [[flat]];       // 使用单调着色模式float pointSize [[point_size]]; // 由顶点着色器指定点的大小
};struct TexturedColorInOut
{float4 position [[ position ]];float2 texCoords;half4  color;float pointSize [[point_size]]; // 由顶点着色器指定点的大小
};struct VertexInfo
{packed_float2 position;packed_float4 colors;
};struct TexturedVertexInfo
{packed_float2 position;packed_float2 textureCoords;packed_float4 colors;
};/*** ortho projection* left = -1.0, right = 1.0, bottom = -1.0, top = 1.0, near = 1.0, far = 3.0*/
static constexpr constant const float4 projectionColumn1 = float4(1.0f, 0.0f, 0.0f, 0.0f);
static constexpr constant const float4 projectionColumn2 = float4(0.0f, 1.0f, 0.0f, 0.0f);
static constexpr constant const float4 protectionColumn3 = float4(0.0f, 0.0f, -1.0f, -2.0f);
static constexpr constant const float4 projectionColumn4 = float4(0.0f, 0.0f, 0.0f, 1.0f);/*** model view translation* x = -0.4, y = 0.0, z = -2.3*/
static constexpr constant const float4 translationColumn1 = float4(1.0f, 0.0f, 0.0f, 0.0f);
static constexpr constant const float4 translationColumn2 = float4(0.0f, 1.0f, 0.0f, 0.0f);
static constexpr constant const float4 translationColumn3 = float4(0.0f, 0.0f, 1.0f, -2.3f);
static constexpr constant const float4 translationColumn4 = float4(0.0f, 0.0f, 0.0f, 1.0f);/// normal vertex shader function
vertex struct ColorInOut point_vertex(device struct VertexInfo* vertex_array [[ buffer(0) ]],constant float *pOffset  [[ buffer(1) ]],unsigned int vid [[ vertex_id ]])
{struct ColorInOut out;auto in_position = float4(float2(vertex_array[vid].position), 0.0f, 1.0f);auto projection = float4x4(projectionColumn1, projectionColumn2, protectionColumn3, projectionColumn4);auto translation = float4x4(translationColumn1, translationColumn2, translationColumn3, translationColumn4);const auto offset = float2(pOffset[2 * vid + 0], pOffset[2 * vid + 1]);translation[0].w = offset.x;translation[1].w = offset.y;out.position = in_position * ((translation * projection));out.color = half4(vertex_array[vid].colors);// 设置点的大小为32个像素out.pointSize = 32.0f;return out;
}// normal fragment shader function
fragment half4 point_fragment(struct ColorInOut in [[stage_in]])
{return in.color;
}// textured vertex shader function
vertex struct TexturedColorInOut
textured_point_vertex(device struct TexturedVertexInfo* vertex_array [[ buffer(0) ]],constant float *pOffset  [[ buffer(1) ]],unsigned int vid [[ vertex_id ]])
{struct TexturedColorInOut out;auto in_position = float4(float2(vertex_array[vid].position), 0.0f, 1.0f);auto projection = float4x4(projectionColumn1, projectionColumn2, protectionColumn3, projectionColumn4);auto translation = float4x4(translationColumn1, translationColumn2, translationColumn3, translationColumn4);const auto offset = float2(pOffset[2 * vid + 0], pOffset[2 * vid + 1]);translation[0].w = offset.x;translation[1].w = offset.y;out.position = in_position * ((translation * projection));out.texCoords = vertex_array[vid].textureCoords;out.color = half4(vertex_array[vid].colors);out.pointSize = 32.0f;return out;
}// textured fragment shader
fragment half4 textured_point_fragment(struct TexturedColorInOut in [[stage_in]],texture2d<float> tex [[ texture(0) ]],sampler texSampler [[ sampler(0) ]],float2 pointCoord [[point_coord]])
{// 关于 [[point_coord]]:// Two-dimensional coordinates indicating where within a point primitive// the current fragment is located.// They range from 0.0 to 1.0 across the point.const auto x = in.texCoords.x + 0.06f * pointCoord.x;const auto y = in.texCoords.y + 0.06f * pointCoord.y;const auto texel = half4(tex.sample(texSampler, float2(x, y)));return half4(in.color * texel.a);
}

最后,我们给出UI相关的ViewController.m源代码:

//
//  ViewController.m
//  MetalPointSprite
//
//  Created by Zenny Chen on 2019/1/23.
//  Copyright © 2019 Zenny Chen. All rights reserved.
//#import "ViewController.h"
#import "MyMetalLayer.h"@implementation ViewController
{
@privateMyMetalLayer *mLayer;
}- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.self.view.wantsLayer = YES;const let viewSize = self.view.frame.size;CGFloat x = 20.0;let y = viewSize.height - 20.0 - 25.0;let button = [NSButton buttonWithTitle:@"Show" target:self action:@selector(showButtonTouched:)];button.frame = NSMakeRect(x, y, 70.0, 25.0);[self.view addSubview:button];x += button.frame.size.width + 20.0;button = [NSButton buttonWithTitle:@"Close" target:self action:@selector(closeButtonTouched:)];button.frame = NSMakeRect(x, y, 70.0, 25.0);[self.view addSubview:button];
}// MARK: 按钮事件处理- (void)showButtonTouched:(NSButton*)sender
{if(mLayer != nil)return;const let x = (self.view.frame.size.width - 512.0) * 0.5;mLayer = MyMetalLayer.new;mLayer.frame = CGRectMake(x, 50.0, 512.0, 512.0);mLayer.contentsScale = NSScreen.mainScreen.backingScaleFactor;[mLayer setup];[self.view.layer addSublayer:mLayer];[mLayer release];
}- (void)closeButtonTouched:(NSButton*)sender
{if(mLayer != nil){[mLayer removeFromSuperlayer];mLayer = nil;}
}- (void)setRepresentedObject:(id)representedObject {[super setRepresentedObject:representedObject];// Update the view, if already loaded.
}@end

展示效果图如下:

metal

大家还有神马问题,欢迎在底下评论~

http://www.ds6.com.cn/news/31179.html

相关文章:

  • 武汉做机床的公司网站国家免费培训学校
  • 惠州品牌网站建设公司哪里有如何找外包的销售团队
  • 腾讯云网站搭建我想自己建立一个网站
  • 福州网站建设方案服务舆情服务公司
  • html5行业网站在线资源搜索神器
  • 2345官网上海网站优化
  • 网站图片链接是怎么做的杭州互联网公司排名榜
  • 徐州好点的做网站的公司专业黑帽seo
  • 工业信息部网站备案网络营销建议
  • 一般通过什么途径当交警优化seo排名
  • phpcms v9网站导航武汉软件测试培训机构排名
  • yellow日本高清免费中文郑州粒米seo外包
  • 做网站别名解析的目的是什么免费域名怎么注册
  • 盐城网站开发天堂网长尾关键词挖掘网站
  • 亚马逊网站开发百度快速收录教程
  • 经典网站模板下载日本域名注册网站
  • 河南网站建设价格大全东营优化公司
  • 淘宝优惠券发布网站怎么做西安网站seo费用
  • 外贸网站交易平台企业网络营销策划书范文
  • 电商网站开发用什么软件好企业网站设计代码
  • 网站设计太原域名注册查询
  • 代办公司注册收费标准南宁百度seo
  • htmlcss做旅游网站中国舆情网
  • 做网站需要源码吗推广方式都有哪些
  • 做视频网站可行吗抖音营销推广怎么做
  • 寿光公司做网站seo外包网站
  • 企业电子商务网站重庆网络推广外包
  • 可画设计软件下载靠谱的seo收费
  • 口碑好的邵阳网站建设百度seo点击
  • 外国人搞笑做视频网站培训计划方案模板