Metal off-screen drawing with Multi-Sampling - ios

How do I render primitives into off screen texture, not directly into the screen?
I have a set of triangles and corresponding color, I just want to draw them the same way I do to screen, but into off screen texture, that I can save into a file.
Can anybody show me a code sample of that?

Ok, I realized it myself. This code does the job, with only exception that it draw too huge triangles, but that is a different topic for Vertex function.
Here is my code:
let fragmentProgram = defaultLibrary.newFunctionWithName("image_fragmentT")
let vertexProgram = defaultLibrary.newFunctionWithName("image_vertexT")
struct VertexT {
var x, y, z, w : Float
var r, g, b, a : Float
}
let vertexDescriptor = MTLVertexDescriptor()
vertexDescriptor.attributes[0].offset = 0
vertexDescriptor.attributes[0].format = .Float4
vertexDescriptor.attributes[0].bufferIndex = 0
vertexDescriptor.attributes[1].offset = 0
vertexDescriptor.attributes[1].format = .Float4
vertexDescriptor.attributes[1].bufferIndex = 0
vertexDescriptor.layouts[0].stepFunction = .PerVertex
vertexDescriptor.layouts[0].stride = sizeof(VertexT)
let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
pipelineStateDescriptor.vertexDescriptor = vertexDescriptor
pipelineStateDescriptor.vertexFunction = vertexProgram
pipelineStateDescriptor.fragmentFunction = fragmentProgram
pipelineStateDescriptor.colorAttachments[0].pixelFormat = .RGBA8Unorm;
pipelineStateDescriptor.colorAttachments[0].blendingEnabled = true
pipelineStateDescriptor.sampleCount = 4
pipelineStateDescriptor.colorAttachments[0].rgbBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].alphaBlendOperation = MTLBlendOperation.Add
pipelineStateDescriptor.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactor.SourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
pipelineStateDescriptor.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactor.OneMinusSourceAlpha
let sampleDesc = MTLTextureDescriptor()
sampleDesc.textureType = MTLTextureType.Type2DMultisample
sampleDesc.width = inTexture.width
sampleDesc.height = inTexture.height
sampleDesc.sampleCount = 4
sampleDesc.pixelFormat = .RGBA8Unorm
sampleDesc.storageMode = .Private
sampleDesc.usage = .RenderTarget
let sampletex = device.device.newTextureWithDescriptor(sampleDesc)
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = sampletex
renderPassDescriptor.colorAttachments[0].resolveTexture = outTexture
renderPassDescriptor.colorAttachments[0].loadAction = .Clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0)
renderPassDescriptor.colorAttachments[0].storeAction = .MultisampleResolve
let renderCB = commandQueue.commandBuffer()
let renderCommandEncoder = renderCB.renderCommandEncoderWithDescriptor(renderPassDescriptor)
let pipelineState = try! device.device.newRenderPipelineStateWithDescriptor(pipelineStateDescriptor)
renderCommandEncoder.setRenderPipelineState(pipelineState)
let vertexBuf = device.device.newBufferWithLength(triangles.count * 3 * sizeof(VertexT), options: .CPUCacheModeDefaultCache)
var vBufPointer = [VertexT]()
for i in 0..<triangles.count {
// create buffer here
}
memcpy(vertexBuf.contents(), &vBufPointer, triangles.count * 3 * sizeof(VertexT))
renderCommandEncoder.setVertexBuffer(vertexBuf, offset: 0, atIndex: 0)
renderCommandEncoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: triangles.count * 3)
renderCommandEncoder.endEncoding()
renderCB.commit()
renderCB.waitUntilCompleted()
You image now is in outTexture variable.

Related

How do I draw yaxis min value as different color on certain limit?

I am using iOS Charts to draw linechart but unable to draw the same graph as shown below in the image. I tried by setting leftAxis.axisMinimum = minValue but it also not working. If the given value is minimum on certain limit it should show in red color as shown in attached image.
func setLineChartWithDateFormat(graphPoints: [GraphPoints], linePosition: CGFloat, minValue: Double = 0, fillColor: UIColor = UIColor(red: 158.0/255.0, green: 188.0/255.0, blue: 136.0/255.0, alpha: 1)) {
var referenceTimeInterval: TimeInterval = 0
if let minTimeInterval = (graphPoints.map { ($0.getDateFromDateTime?.timeIntervalSince1970 ?? 0) }).min() {
referenceTimeInterval = minTimeInterval
}
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.dateFormat = "MMM dd"
let xValuesNumberFormatter = ChartXAxisWithFormatter(referenceTimeInterval: referenceTimeInterval, dateFormatter: formatter)
// Define chart entries
var entries = [ChartDataEntry]()
var xAxisValues = [CGFloat]()
for object in graphPoints {
let timeInterval = object.getDateFromDateTime?.timeIntervalSince1970 ?? 0
let xValue = (timeInterval - referenceTimeInterval) / (3600 * 24)
xAxisValues.append(CGFloat(xValue))
let yValue = object.price ?? 0
let entry = ChartDataEntry(x: xValue, y: yValue)
entry.setValue(object.getDateFromDateTime, forKey: "data")
entries.append(entry)
}
var minValue = xAxisValues.min()
let chartDataSet = LineChartDataSet(entries: entries, label: "BPM")
chartDataSet.fillColor = fillColor
/// Data object that encapsulates all data associated with a LineChart.
let chartData = LineChartData()
chartData.addDataSet(chartDataSet)
chartData.setDrawValues(false)
chartDataSet.drawCirclesEnabled = false
chartDataSet.mode = .linear
chartDataSet.colors = [.white]
chartDataSet.fillFormatter = LineChartFillFormatter(value: minValue ?? 0)
// let gradientColors = [gradientColor1.cgColor, gradientColor2.cgColor] as CFArray
// let colorLocations: [CGFloat] = [0.7,0.0]
// guard CGGradient.init(colorsSpace: CGColorSpaceCreateDeviceRGB(), colors: gradientColors, locations: colorLocations) != nil else {
// print("Gradient Error")
// return
// }
chartDataSet.drawFilledEnabled = true
chartDataSet.fillAlpha = 1
chartDataSet.setDrawHighlightIndicators(true)
chartDataSet.drawHorizontalHighlightIndicatorEnabled = false
// lineChart.leftAxis.axisMinimum = minValue
///x axis for chart view
let xAxis: XAxis = self.lineChart.xAxis
xAxis.labelPosition = .bottomInside
xAxis.drawGridLinesEnabled = true //true, if want x Axis grid lines
xAxis.valueFormatter = xValuesNumberFormatter
xAxis.gridColor = gridColor
xAxis.axisLineColor = gridColor
xAxis.labelFont = BSFonts.getFontWithSize(fontType: .iBMPlexSansCond, fontSize: BSFontSize.small)
xAxis.labelTextColor = axisTextColor
xAxis.labelPosition = .bottom
xAxis.spaceMin = 0.5
xAxis.drawAxisLineEnabled = false
xAxis.drawLimitLinesBehindDataEnabled = true
xAxis.granularityEnabled = true
// xAxis.granularity = 2
xAxis.labelWidth = UIDevice.current.isIPad ? 30 : 20
// xAxis.labelCount = 4
///right axis for chart view
let rightAxis: YAxis = lineChart.rightAxis
rightAxis.enabled = false
///y axis of chart view
let leftAxis: YAxis = lineChart.leftAxis
leftAxis.labelPosition = .insideChart
leftAxis.drawGridLinesEnabled = true //true, if want y axis grid lines
leftAxis.drawLabelsEnabled = true
leftAxis.gridColor = gridColor
leftAxis.axisLineColor = gridColor
leftAxis.labelFont = BSFonts.getFontWithSize(fontType: .iBMPlexSansCond, fontSize: BSFontSize.small)
leftAxis.labelTextColor = axisTextColor
leftAxis.labelPosition = .outsideChart
//leftAxis.spaceBottom = 0.3
leftAxis.drawAxisLineEnabled = false
leftAxis.labelCount = 4
/// The data for the chart
lineChart.data = chartData
lineChart.doubleTapToZoomEnabled = false
lineChart.data?.highlightEnabled = true
}

how to pass time as xAxis data value in method numberForPlot using core plot?

I need to create graph which is "Heart rate vs time". in xAxis I am showing time which is in format of "HH:mm:ss" and at y Axis its decimal value. My code is as below :
data source for graph:
var graphData:[(String,Double)] = []
let data = ("17:56:35",Double(65))
let data1 = ("18:04:13",Double(95))
let data2 = ("18:8:35",Double(25))
self.graphData.append(data)
self.graphData.append(data1)
self.graphData.append(data2)
Set up graph and plot data code:
func setGraph() {
let tts = CPTMutableTextStyle()
tts.fontSize = 75.0
tts.color = CPTColor(CGColor: UIColor.blackColor().CGColor)
tts.fontName = "HelveticaNeue-Bold"
self.graph.titleTextStyle = tts
self.graph.title = "Heart Rate vs Time"
self.graph.applyTheme(CPTTheme(named:kCPTPlainWhiteTheme))
let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace!
plotSpace.allowsUserInteraction = false
let xRange = plotSpace.xRange.mutableCopy() as! CPTMutablePlotRange
xRange.locationDouble = Double(0)
xRange.lengthDouble = Double(1000)
plotSpace.xRange = xRange
let yRange = plotSpace.yRange.mutableCopy() as! CPTMutablePlotRange
yRange.locationDouble = Double(0)
yRange.lengthDouble = Double(210)
plotSpace.yRange = yRange
graph.addPlotSpace(plotSpace)
graph.plotAreaFrame!.paddingTop = 0
graph.plotAreaFrame!.paddingRight = 0
graph.plotAreaFrame!.paddingBottom = graphOffset
graph.plotAreaFrame!.paddingLeft = graphOffset
graph.plotAreaFrame!.masksToBorder = false
// Grid line styles
let majorLineStyle = CPTMutableLineStyle()
majorLineStyle.lineWidth = 0.75
majorLineStyle.lineColor = CPTColor.redColor()
let minorLineStyle = CPTMutableLineStyle()
minorLineStyle.lineWidth = 0.25
minorLineStyle.lineColor = CPTColor.blackColor()
//Axis Line colors
let axisLineStyle = CPTMutableLineStyle()
axisLineStyle.lineWidth = 2.0
axisLineStyle.lineColor = CPTColor.blackColor()
//Axis Label colors
let labelTextStyle = CPTMutableTextStyle()
labelTextStyle.textAlignment = CPTTextAlignment.Left
labelTextStyle.color = CPTColor.blackColor()
//Axis title color
let titleTextStyle = CPTMutableTextStyle()
titleTextStyle.textAlignment = CPTTextAlignment.Left
titleTextStyle.color = CPTColor.blackColor()
titleTextStyle.fontSize = 15
let dataSourceLinePlot = CPTScatterPlot()
let lineStyle = CPTMutableLineStyle()
lineStyle.lineWidth = 3.0
lineStyle.lineColor = CPTColor.blueColor()
dataSourceLinePlot.dataLineStyle = lineStyle
dataSourceLinePlot.identifier = kPlotIdentifier
// dataSourceLinePlot.dataSource = self
let xts = CPTMutableTextStyle()
xts.color = CPTColor(componentRed: 255.0, green: 255.0, blue: 255.0, alpha: 1.0)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let timeformatter = CPTTimeFormatter(dateFormatter: dateFormatter)
timeformatter.referenceDate = NSDate()
let axisSet = graph.axisSet as! CPTXYAxisSet!
let xAxis = axisSet.xAxis as CPTXYAxis!
xAxis.axisTitle = CPTAxisTitle(text: "Elapsed Time", textStyle: xts)
xAxis.labelFormatter = timeformatter
xAxis.majorGridLineStyle = majorLineStyle
xAxis.minorGridLineStyle = minorLineStyle
xAxis.majorIntervalLength = NSNumber(double: 60.0)
let yAxis = axisSet.yAxis as CPTXYAxis!
yAxis.axisTitle = CPTAxisTitle(text: "Heart Rate", textStyle: xts)
let axisFormatter = NSNumberFormatter()
axisFormatter.maximumFractionDigits = 0
axisFormatter.minimumIntegerDigits = 1
yAxis.labelFormatter = axisFormatter
yAxis.titleOffset = 35.0
yAxis.majorIntervalLength = 20
yAxis.majorGridLineStyle = majorLineStyle
yAxis.minorGridLineStyle = minorLineStyle
graph.addPlot(dataSourceLinePlot)
self.IBviewGraph.hostedGraph = self.graph
}
To plot data :
func setGraphData() {
dispatch_sync(dispatch_get_main_queue(), {
let plot = CPTScatterPlot()
plot.dataSource = self
let actualPlotStyle = plot.dataLineStyle!.mutableCopy() as! CPTMutableLineStyle
actualPlotStyle.lineWidth = 2.0
actualPlotStyle.lineColor = CPTColor(CGColor: (UIColor.yellowColor().CGColor))
plot.dataLineStyle = actualPlotStyle
plot.interpolation = .Linear
self.graph.addPlot(plot)
})
}
Data source methods
func numberOfRecordsForPlot(plot: CPTPlot) -> UInt {
return 3
}
func numberForPlot(plot: CPTPlot, field: UInt, recordIndex: UInt) -> AnyObject?{
var num = NSNumber()
let index = Int(recordIndex)
switch field {
case 0://xaxis
return graphData[index].0
case 1://yaxis
return graphData[index].1
default:
break
}
print("coordintes = ",num)
return num
}
My graph is drown but only yAxis points are correct , its not considering xAxis value and so because of that points are at wrong position in graph. I think the value I am passing in method numberForPlot for xAxis is wrong , but don't know how to pass it. Give me some solution.
The x-values in the graphData array are strings. Core Plot expects the datasource to return a numeric value for each index. It's reading the hour from the time string and using that for the x-value.
The x-axis is configured to display numbers in the range 0 to 1,000. Therefore, you need to convert the time data to a number in that range. Keep track of the reference date used to format the axis labels (i.e., store it in an instance variable) and use that to convert the time data to an offset in seconds from the reference date.

Graph plot reference point is changing

I have a weird issue. I have some data on graph and when new data arrives I am inserting that into graph. But issue is new data plotting is starting from first point of already drawn plot not from last point of plot. The console prints the correct point but the plotting starts from first point of already drawn plot not from last point of already drawn plot. My code for graph set up is here :Code for graph set up and Code for Axis animation
code :
func setGraph() {
let tts = CPTMutableTextStyle()
tts.fontSize = 75.0
tts.color = CPTColor(CGColor: UIColor.blackColor().CGColor)
tts.fontName = "HelveticaNeue-Bold"
self.graph.titleTextStyle = tts
self.graph.title = "Heart Rate vs Time"
self.graph.applyTheme(CPTTheme(named:kCPTPlainWhiteTheme))
let plotSpace = graph.defaultPlotSpace as! CPTXYPlotSpace!
plotSpace.allowsUserInteraction = false
let xRange = plotSpace.xRange.mutableCopy() as! CPTMutablePlotRange
xRange.locationDouble = Double(0)
xRange.lengthDouble = Double(kMaxDataPoints)
plotSpace.xRange = xRange
let yRange = plotSpace.yRange.mutableCopy() as! CPTMutablePlotRange
yRange.locationDouble = Double(0)
yRange.lengthDouble = Double(210)
plotSpace.yRange = yRange
graph.addPlotSpace(plotSpace)
graph.plotAreaFrame!.paddingTop = 0
graph.plotAreaFrame!.paddingRight = 0
graph.plotAreaFrame!.paddingBottom = graphOffset
graph.plotAreaFrame!.paddingLeft = graphOffset
graph.plotAreaFrame!.masksToBorder = false
// Grid line styles
let majorLineStyle = CPTMutableLineStyle()
majorLineStyle.lineWidth = 0.75
majorLineStyle.lineColor = CPTColor.redColor()
let minorLineStyle = CPTMutableLineStyle()
minorLineStyle.lineWidth = 0.25
minorLineStyle.lineColor = CPTColor.blackColor()
//Axis Line colors
let axisLineStyle = CPTMutableLineStyle()
axisLineStyle.lineWidth = 2.0
axisLineStyle.lineColor = CPTColor.blackColor()
//Axis Label colors
let labelTextStyle = CPTMutableTextStyle()
labelTextStyle.textAlignment = CPTTextAlignment.Left
labelTextStyle.color = CPTColor.blackColor()
//Axis title color
let titleTextStyle = CPTMutableTextStyle()
titleTextStyle.textAlignment = CPTTextAlignment.Left
titleTextStyle.color = CPTColor.blackColor()
titleTextStyle.fontSize = 15
let dataSourceLinePlot = CPTScatterPlot()
let lineStyle = CPTMutableLineStyle()
lineStyle.lineWidth = 3.0
lineStyle.lineColor = CPTColor.blueColor()
dataSourceLinePlot.dataLineStyle = lineStyle
dataSourceLinePlot.identifier = kPlotIdentifier
// dataSourceLinePlot.dataSource = self
let xts = CPTMutableTextStyle()
xts.color = CPTColor(componentRed: 255.0, green: 255.0, blue: 255.0, alpha: 1.0)
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "HH:mm:ss"
let timeformatter = CPTTimeFormatter(dateFormatter: dateFormatter)
timeformatter.referenceDate = NSDate().dateByAddingTimeInterval(startTimeInterval)
currentdate = timeformatter.referenceDate
let axisSet = graph.axisSet as! CPTXYAxisSet!
let xAxis = axisSet.xAxis as CPTXYAxis!
xAxis.axisTitle = CPTAxisTitle(text: "Elapsed Time", textStyle: xts)
xAxis.labelFormatter = timeformatter
xAxis.majorGridLineStyle = majorLineStyle
xAxis.minorGridLineStyle = minorLineStyle
xAxis.majorIntervalLength = NSNumber(double: 30.0)
let yAxis = axisSet.yAxis as CPTXYAxis!
yAxis.axisTitle = CPTAxisTitle(text: "Heart Rate", textStyle: xts)
let axisFormatter = NSNumberFormatter()
axisFormatter.maximumFractionDigits = 0
axisFormatter.minimumIntegerDigits = 1
yAxis.labelFormatter = axisFormatter
yAxis.titleOffset = 35.0
yAxis.majorIntervalLength = 20
yAxis.majorGridLineStyle = majorLineStyle
yAxis.minorGridLineStyle = minorLineStyle
yAxis.axisConstraints = CPTConstraints(lowerOffset: 0.0)
graph.addPlot(dataSourceLinePlot)
self.IBviewGraph.hostedGraph = self.graph
setGraphData()
}
func setGraphData()
{
self.plot.dataSource = self
let actualPlotStyle = self.plot.dataLineStyle!.mutableCopy() as! CPTMutableLineStyle
actualPlotStyle.lineWidth = 2.0
actualPlotStyle.lineColor = CPTColor(CGColor: (UIColor.blueColor().CGColor))
self.plot.dataLineStyle = actualPlotStyle
self.plot.interpolation = .Curved
self.graph.addPlot(self.plot)
}
Now when i receive new data :
graphData.removeAtIndex(0)
plot.deleteDataInIndexRange(NSMakeRange(0, 1))
let location = currentIndex
let oldRange = CPTPlotRange(location: location , length: kMaxDataPoints)
let newRange = CPTPlotRange(location: location + 30, length: kMaxDataPoints)
CPTAnimation.animate(plotSpace, property: "xRange", fromPlotRange: oldRange, toPlotRange: newRange, duration: CGFloat(1.0 / kFrameRate))
graphData.append((newTime,data.1))
plot.insertDataAtIndex(UInt(graphData.count - 1), numberOfRecords: 1)

Metal render with 2 pipelines, the second object overlap the first object

I have 2 objects, one is with texture, the other one is without texture. I use 2 shaders and I use 2 render pipelines to draw the 2 objects. the 2 object are drawn fine...but when the second obejct is drawn ...it overlaps the first object, so only the second object is on screen... don't know where goes wrong...Maybe because the pipeline is not used right, in the last part of the draw() function. the model draws properly first, but when the sky is drew, the model disappeared...Please help me,,,Thanks
Here is my code:
class PanoViewController: GameViewController {
//sky
var depthStencilState: MTLDepthStencilState! = nil
var vertexBufferSky: MTLBuffer! = nil
var uniformBufferSky: MTLBuffer! = nil
var depthTextureSky: MTLTexture! = nil
var diffuseTextureSky: MTLTexture! = nil
var samplerStateSky: MTLSamplerState! = nil
//model
var depthStencilStateModel: MTLDepthStencilState! = nil
var vertexBufferModel: MTLBuffer! = nil
var normalBufferModel: MTLBuffer! = nil
var colorBufferModel: MTLBuffer! = nil
var uniformBufferModel: MTLBuffer! = nil
override func buildPipeline() {
//Model
let library = device!.newDefaultLibrary()!
//pipeline descriptor
// buildPipelinForSky(library)
// buildPipelineForModel(library)
//pipeline
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(buildPipelinForSky(library))
pipelineModel = try device!.newRenderPipelineStateWithDescriptor(buildPipelineForModel(library))
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
}
func buildPipelineForModel(library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let pipeLineDesc = MTLRenderPipelineDescriptor()
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
pipeLineDesc.vertexFunction = vertexFunctionModel
pipeLineDesc.vertexDescriptor = vertexDescriptorModel
pipeLineDesc.fragmentFunction = fragmentFunctionModel
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
return pipeLineDesc
}
func buildPipelinForSky(library: MTLLibrary ) -> MTLRenderPipelineDescriptor{
let pipeLineDesc = MTLRenderPipelineDescriptor()
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
pipeLineDesc.vertexFunction = vertexFunctionSky
pipeLineDesc.vertexDescriptor = vertexDescriptorSky
pipeLineDesc.fragmentFunction = fragmentFunctionSky
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
pipeLineDesc.depthAttachmentPixelFormat = .Depth32Float
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
return pipeLineDesc
}
override func buildResources() {
//Model
(vertexBufferModel,normalBufferModel,colorBufferModel) = PointCloud.model(device!)
uniformBufferModel = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
//Sky
vertexBufferSky = SkySphere.sphere(device!)
uniformBufferSky = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
diffuseTextureSky = self.textureForImage(UIImage(named: "bluemarble")!, device: device!)
}
override func resize() {
//Model
super.resize()
//Sky
let layerSizeSky = metalLayer.drawableSize
let depthTextureDescriptorSky = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.Depth32Float,
width: Int(layerSizeSky.width),
height: Int(layerSizeSky.height),
mipmapped: false)
depthTextureSky = device!.newTextureWithDescriptor(depthTextureDescriptorSky)
}
override func draw() {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
//command buffer
let commandBuffer = commandQueue.commandBuffer()
commandBuffer.addCompletedHandler{ [weak self] commandBuffer in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//model
var passDescriptor = MTLRenderPassDescriptor()
passDescriptor = passDescrForModel(drawable,passDescriptor: passDescriptor)
var commandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoder.pushDebugGroup("model pass")
commandEncoder.label = "model buffer"
pointCloudDraw(commandEncoder)
commandEncoder.endEncoding()
commandEncoder.popDebugGroup()
passDescriptor = passDescrForSky(drawable,passDescriptor: passDescriptor)
commandEncoder = commandBuffer.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoder.pushDebugGroup("sky pass")
commandEncoder.label = "sky buffer"
skyDraw(commandEncoder)
commandEncoder.popDebugGroup()
commandEncoder.endEncoding()
commandBuffer.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBuffer.commit()
}
}
func passDescrForModel(drawable: CAMetalDrawable, passDescriptor:MTLRenderPassDescriptor) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
// passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
return passDescriptor
}
func passDescrForSky(drawable: CAMetalDrawable, passDescriptor:MTLRenderPassDescriptor) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
// passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.depthAttachment.texture = depthTextureSky
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .DontCare
return passDescriptor
}
func pointCloudDraw(commandencodeModel: MTLRenderCommandEncoder){
commandencodeModel.setRenderPipelineState(pipelineModel)
commandencodeModel.setDepthStencilState(depthStencilState)
commandencodeModel.setFrontFacingWinding(.CounterClockwise)
commandencodeModel.setCullMode(.Back)
commandencodeModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandencodeModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandencodeModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandencodeModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandencodeModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandencodeModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
}
func skyDraw(commandencodeSky: MTLRenderCommandEncoder) {
commandencodeSky.setRenderPipelineState(pipelineSky)
commandencodeSky.setDepthStencilState(depthStencilState)
commandencodeSky.setFrontFacingWinding(.CounterClockwise)
commandencodeSky.setCullMode(.Back)
commandencodeSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandencodeSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandencodeSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandencodeSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandencodeSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
}
}
Change the passDescrForSky function passDescriptor.colorAttachments[0].loadAction = .Load
Then the 2 objects are all shows up...Something happen to the position...the first one needs to be adjusted...

Metal draw one object with texture one object without texture

I want to render 2 different objects with Metal...one is with texture, the other one is without texture. I have 2 different shaders, 2 different vertex descriptors, is that means i should use 2 different render pipeline? .. There is only one object drawing (the model with out texture)on the screen correct, the other one is wrong, I don't know where I went wrong.... Here is the code:
override func buildPipeline() {
//Model
let library = device!.newDefaultLibrary()!
let pipelineDescriptor = MTLRenderPipelineDescriptor()
buildPipelinForSky(pipelineDescriptor, library: library)
buildPipelineForModel(pipelineDescriptor, library: library)
do {
pipelineSky = try device!.newRenderPipelineStateWithDescriptor(pipelineDescriptor)
} catch {
print("error with device.newRenderPipelineStateWithDescriptor")
}
let depthStencilDescriptor = MTLDepthStencilDescriptor()
depthStencilDescriptor.depthCompareFunction = .Less
depthStencilDescriptor.depthWriteEnabled = true
depthStencilState = device!.newDepthStencilStateWithDescriptor(depthStencilDescriptor)
commandQueue = device!.newCommandQueue()
}
func buildPipelineForModel(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary) -> MTLRenderPipelineDescriptor {
let vertexFunctionModel = library.newFunctionWithName("vertex_ply")
let fragmentFunctionModel = library.newFunctionWithName("fragment_ply")
let vertexDescriptorModel = MTLVertexDescriptor()
vertexDescriptorModel.attributes[0].offset = 0
vertexDescriptorModel.attributes[0].format = .Float4
vertexDescriptorModel.attributes[0].bufferIndex = 0
vertexDescriptorModel.layouts[0].stepFunction = .PerVertex
vertexDescriptorModel.layouts[0].stride = sizeof(Float) * 4
pipeLineDesc.vertexFunction = vertexFunctionModel
pipeLineDesc.vertexDescriptor = vertexDescriptorModel
pipeLineDesc.fragmentFunction = fragmentFunctionModel
pipeLineDesc.colorAttachments[0].pixelFormat = .BGRA8Unorm
return pipeLineDesc
}
func buildPipelinForSky(pipeLineDesc:MTLRenderPipelineDescriptor, library: MTLLibrary ) -> MTLRenderPipelineDescriptor{
let vertexFunctionSky = library.newFunctionWithName("vertex_sky")
let fragmentFunctionSky = library.newFunctionWithName("fragment_sky")
let vertexDescriptorSky = MTLVertexDescriptor()
vertexDescriptorSky.attributes[0].offset = 0
vertexDescriptorSky.attributes[0].format = .Float4
vertexDescriptorSky.attributes[0].bufferIndex = 0
vertexDescriptorSky.attributes[1].offset = sizeof(Float32) * 4
vertexDescriptorSky.attributes[1].format = .Float4
vertexDescriptorSky.attributes[1].bufferIndex = 0
vertexDescriptorSky.attributes[2].offset = sizeof(Float32) * 8
vertexDescriptorSky.attributes[2].format = .Float2
vertexDescriptorSky.attributes[2].bufferIndex = 0
vertexDescriptorSky.layouts[0].stepFunction = .PerVertex
vertexDescriptorSky.layouts[0].stride = sizeof(Vertex)
pipeLineDesc.vertexFunction = vertexFunctionSky
pipeLineDesc.vertexDescriptor = vertexDescriptorSky
pipeLineDesc.fragmentFunction = fragmentFunctionSky
pipeLineDesc.depthAttachmentPixelFormat = .Depth32Float
let samplerDescriptorSky = MTLSamplerDescriptor()
samplerDescriptorSky.minFilter = .Nearest
samplerDescriptorSky.magFilter = .Linear
samplerStateSky = device!.newSamplerStateWithDescriptor(samplerDescriptorSky)
return pipeLineDesc
}
override func buildResources() {
// (vertexBuffer, indexBuffer) = SphereGenerator.sphereWithRadius(1, stacks: 30, slices: 30, device: device!)
//Model
(vertexBufferModel,normalBufferModel,colorBufferModel) = PointCloud.model(device!)
uniformBufferModel = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
//Sky
vertexBufferSky = SkySphere.sphere(device!)
uniformBufferSky = device!.newBufferWithLength(sizeof(M4f) * 2, options: .OptionCPUCacheModeDefault)
diffuseTextureSky = self.textureForImage(UIImage(named: "bluemarble")!, device: device!)
}
override func resize() {
//Model
super.resize()
//Sky
let layerSizeSky = metalLayer.drawableSize
let depthTextureDescriptorSky = MTLTextureDescriptor.texture2DDescriptorWithPixelFormat(.Depth32Float,
width: Int(layerSizeSky.width),
height: Int(layerSizeSky.height),
mipmapped: false)
depthTextureSky = device!.newTextureWithDescriptor(depthTextureDescriptorSky)
}
override func draw() {
dispatch_semaphore_wait(inflightSemaphore, DISPATCH_TIME_FOREVER)
//Sky
if let drawable = metalLayer.nextDrawable()
{
var modelMatrixTransSky = M4f()
var modelMatrixRotSky = M4f()
var modelMatrixScaleSky = M4f()
modelMatrixTransSky = translate(0, y: 0, z: 0)
modelMatrixRotSky = rotate(90, r: V3f(1,0,0)) * modelMatrixRotSky
modelMatrixScaleSky = scaling(10, y: 10, z: 10)
let modelMatrixSky = modelMatrixTransSky * modelMatrixRotSky * modelMatrixScaleSky
var viewMatrixSky = M4f()
viewMatrixSky = myCamera.setLookAt(viewMatrixSky)
let modelViewMatrixSky = viewMatrixSky * modelMatrixSky
let aspect = Float32(metalLayer.drawableSize.width) / Float32(metalLayer.drawableSize.height)
let kFOVY:Float = 85.0
let projectionMatrix = perspective_fov(kFOVY, aspect: aspect, near: 0.1, far: 180.0)
let matricesSky = [projectionMatrix, modelViewMatrixSky]
memcpy(uniformBufferSky.contents(), matricesSky, Int(sizeof(M4f) * 2))
let commandBufferSky = commandQueue.commandBuffer()
commandBufferSky.addCompletedHandler{ [weak self] commandBufferSky in
if let strongSelf = self {
dispatch_semaphore_signal(strongSelf.inflightSemaphore)
}
return
}
//Model
var modelMatrixTransModel = M4f()
var modelMatrixRotModel = M4f()
var modelMatrixScaleModel = M4f()
modelMatrixTransModel = translate(0, y: 0, z: 0)
modelMatrixRotModel = rotate(0, r: V3f(1,0,0))
modelMatrixScaleModel = scaling(10, y: 10, z: 10)
let modelMatrixModel = modelMatrixTransModel * modelMatrixRotModel * modelMatrixScaleModel
var viewMatrixModel = M4f()
viewMatrixModel = myCamera.setLookAt(viewMatrixModel)
let modelViewMatrixModel = viewMatrixModel * modelMatrixModel
let matricesModel = [projectionMatrix, modelViewMatrixModel]
memcpy(uniformBufferModel.contents(), matricesModel, Int(sizeof(M4f) * 2))
//Sky
let passDescriptor = MTLRenderPassDescriptor()
passDescrForSky(passDescriptor, drawable: drawable)
passDescrForModel(passDescriptor, drawable: drawable)
//Sky
let commandEncoderSky = commandBufferSky.renderCommandEncoderWithDescriptor(passDescriptor)
commandEncoderSky.setRenderPipelineState(pipelineSky)
commandEncoderSky.setDepthStencilState(depthStencilState)
commandEncoderSky.setFrontFacingWinding(.CounterClockwise)
commandEncoderSky.setCullMode(.Back)
pointCloudDraw(commandEncoderSky)
skyDraw(commandEncoderSky)
commandEncoderSky.endEncoding()
commandBufferSky.presentDrawable(drawable)
// bufferIndex matches the current semaphore controled frame index to ensure writing occurs at the correct region in the vertex buffer
bufferIndex = (bufferIndex + 1) % MaxBuffers
commandBufferSky.commit()
}
}
func passDescrForModel(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
return passDescriptor
}
func passDescrForSky(passDescriptor: MTLRenderPassDescriptor, drawable: CAMetalDrawable) -> MTLRenderPassDescriptor{
passDescriptor.colorAttachments[0].texture = drawable.texture
passDescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0.5, 0.5, 0.5, 1)
passDescriptor.colorAttachments[0].loadAction = .Clear
passDescriptor.colorAttachments[0].storeAction = .Store
passDescriptor.depthAttachment.texture = depthTextureSky
passDescriptor.depthAttachment.clearDepth = 1
passDescriptor.depthAttachment.loadAction = .Clear
passDescriptor.depthAttachment.storeAction = .DontCare
return passDescriptor
}
func pointCloudDraw(commandencodeModel: MTLRenderCommandEncoder) {
commandencodeModel.setVertexBuffer(vertexBufferModel, offset:0, atIndex:0)
commandencodeModel.setVertexBuffer(normalBufferModel, offset:0, atIndex:1)
commandencodeModel.setVertexBuffer(colorBufferModel, offset:0, atIndex:2)
commandencodeModel.setVertexBuffer(uniformBufferModel, offset:0, atIndex:3)
commandencodeModel.setFragmentBuffer(uniformBufferModel, offset: 0, atIndex: 0)
commandencodeModel.drawPrimitives(.Point, vertexStart: 0, vertexCount: vertextCountModel)
}
func skyDraw(commandencodeSky: MTLRenderCommandEncoder) {
commandencodeSky.setVertexBuffer(vertexBufferSky, offset:0, atIndex:0)
commandencodeSky.setVertexBuffer(uniformBufferSky, offset:0, atIndex:1)
commandencodeSky.setFragmentTexture(diffuseTextureSky, atIndex: 0)
commandencodeSky.setFragmentSamplerState(samplerStateSky, atIndex: 0)
commandencodeSky.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCountSky)
}
here is the vertex buffer for the sky:
struct Vector4
{
var x: Float32
var y: Float32
var z: Float32
var w: Float32
}
struct TexCoords
{
var u: Float32
var v: Float32
}
struct Vertex
{
var position: Vector4
var normal: Vector4
var texCoords: TexCoords
}
var vertexCountSky: Int = 0
struct SkySphere
{
static func sphere(device: MTLDevice) -> (MTLBuffer!)
{
let ply = plyVntReader.init(objFileName: "test")
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(Vertex) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.vertices)
vertexCountSky = ply!.vertexCount
return (vertexBuffer)
}
}
And here is vertex buffer for the model:
var vertextCountModel: Int = 0
struct PointCloud
{
static func model(device: MTLDevice) -> (MTLBuffer!, MTLBuffer!, MTLBuffer!)
{
let ply = plyVncReader.init(objFileName: "controller_ascii")
vertextCountModel = ply!.vertexCount
let vertexBuffer = device.newBufferWithBytes(ply!.vertices, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let normalBuffer = device.newBufferWithBytes(ply!.normals, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
let colorBuffer = device.newBufferWithBytes(ply!.colors, length:sizeof(V4f) * ply!.vertexCount, options:.OptionCPUCacheModeDefault)
print(ply!.colors)
return (vertexBuffer, normalBuffer, colorBuffer)
}
}
shaders for the sky
using namespace metal;
//Sky
struct TexturedInVertex
{
packed_float4 position [[attribute(0)]];
packed_float4 normal [[attribute(1)]];
packed_float2 texCoords [[attribute(2)]];
};
struct TexturedColoredOutVertex
{
float4 position [[position]];
float3 normal;
float2 texCoords;
float pointsize[[point_size]];
};
struct UniformsSky
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex TexturedColoredOutVertex vertex_sky(device TexturedInVertex *vert [[buffer(0)]],
constant UniformsSky &uniforms [[buffer(1)]],
uint vid [[vertex_id]])
{
float4x4 MV = uniforms.modelViewMatrix;
float3x3 normalMatrix(MV[0].xyz, MV[1].xyz, MV[2].xyz);
float4 modelNormal = vert[vid].normal;
TexturedColoredOutVertex outVertex;
outVertex.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * float4(vert[vid].position);
outVertex.normal = normalMatrix * modelNormal.xyz;
outVertex.texCoords = vert[vid].texCoords;
outVertex.pointsize = 10.0;
return outVertex;
};
fragment half4 fragment_sky(TexturedColoredOutVertex vert [[stage_in]],
texture2d<float, access::sample> diffuseTexture [[texture(0)]],
sampler samplr [[sampler(0)]])
{
float4 diffuseColor = diffuseTexture.sample(samplr, vert.texCoords);
return half4(diffuseColor.r, diffuseColor.g, diffuseColor.b, 1);
};
here is shader for the model
//model
struct ColoredVertex
{
float4 position [[position]];
float4 normal;
float4 color;
float pointsize[[point_size]];
};
struct UniformsPoint
{
float4x4 projectionMatrix;
float4x4 modelViewMatrix;
};
vertex ColoredVertex vertex_ply(constant float4 *position [[buffer(0)]],
constant float4 *normal [[buffer(1)]],
constant float4 *color [[buffer(2)]],
constant UniformsPoint &uniforms [[buffer(3)]],
uint vid [[vertex_id]])
{
ColoredVertex vert;
vert.position = uniforms.projectionMatrix * uniforms.modelViewMatrix * position[vid];
vert.normal = normal[vid];
vert.color = color[vid];
vert.pointsize = 5.0;
return vert;
}
fragment float4 fragment_ply(ColoredVertex vert [[stage_in]])
{
return vert.color;
}

Resources