2016-11-22 2 views
3

Je dessine 2 différents tampons de vertex en métal, l'un avec une texture (en ignorant les données de couleur vertex) et l'autre sans texture (dessin purement la couleur vertex données):Textures et pas de textures en même temps dans le métal, avec mélange multiple

let commandBuffer = self.commandQueue.makeCommandBuffer() 
let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: rpd) 

//render first buffer with texture 
commandEncoder.setRenderPipelineState(self.rps) 
commandEncoder.setVertexBuffer(self.vertexBuffer1, offset: 0, at: 0) 
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1) 
commandEncoder.setFragmentTexture(self.texture, at: 0) 
commandEncoder.setFragmentSamplerState(self.samplerState, at: 0) 
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count1, instanceCount: 1) 


//render second buffer without texture 
commandEncoder.setRenderPipelineState(self.rps) 
commandEncoder.setVertexBuffer(self.vertexBuffer2, offset: 0, at: 0) 
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1) 
commandEncoder.setFragmentTexture(nil, at: 0) 
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count2, instanceCount: 1) 

commandEncoder.endEncoding() 
commandBuffer.present(drawable) 
commandBuffer.commit() 

Le shader ressemble à ceci:

#include <metal_stdlib> 
using namespace metal; 

struct Vertex { 
    float4 position [[position]]; 
    float4 color; 
    float4 texCoord; 
}; 

struct Uniforms { 
    float4x4 modelMatrix; 
}; 

vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]], 
          constant Uniforms &uniforms [[buffer(1)]], 
          uint vid [[vertex_id]]) 
{ 
    float4x4 matrix = uniforms.modelMatrix; 
    Vertex in = vertices[vid]; 
    Vertex out; 
    out.position = matrix * float4(in.position); 
    out.color = in.color; 
    out.texCoord = in.texCoord; 
    return out; 
} 

fragment float4 fragment_func(Vertex vert [[stage_in]], 
           texture2d<float> tex2D  [[ texture(0) ]], 
           sampler   sampler2D [[ sampler(0) ]]) { 

    if (vert.color[0] == 0 && vert.color[1] == 0 && vert.color[2] == 0) { 
     //texture color 
     return tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1])); 
    } 
    else { 
     //color color 
     return vert.color; 
    } 

} 

Est-ce là une meilleure façon de le faire? Tout sommet que je veux utiliser la texture que je suis en noir, et le shader vérifie si la couleur est noire, et si c'est le cas, utilisez la texture, sinon utilisez la couleur.

De même, existe-t-il un moyen de mélanger les polys colorés et les polys texturés ensemble en utilisant une fonction de multiplication s'ils se chevauchent sur l'écran? Il semble que MTLBlendOperation n'a d'options que pour ajouter/soustraire/min/max, pas de multiplier?

+0

Je suis nouveau en métal et a été d'avoir du mal à utiliser une utilisation plus d'une texture sur un tableau de vertex cette question va m'aider à le faire, je pense. – Hope

Répondre

1

Une autre façon de le faire serait d'avoir deux fonctions de fragment différentes, une qui rend les fragments texturés et une autre qui traite des sommets colorés.

D'abord, vous devez créer deux différents MTLRenderPipelineState au moment du chargement:

let desc = MTLRenderPipelineDescriptor()  

/* ...load all other settings in the descriptor... */ 

// Load the common vertex function. 
desc.vertexFunction = library.makeFunction(name: "vertex_func") 

// First create the one associated to the textured fragment function. 
desc.fragmentFunction = library.makeFunction(name: "fragment_func_textured") 
let texturedRPS = try! device.makeRenderPipelineState(descriptor: desc) 

// Then modify the descriptor to create the state associated with the untextured fragment function. 
desc.fragmentFunction = library.makeFunction(name: "fragment_func_untextured") 
let untexturedRPS = try! device.makeRenderPipelineState(descriptor: desc) 

Puis au moment du rendu, avant de coder les commandes de dessin d'un objet texturé vous définissez l'état de texture, et avant de coder les commandes de dessin d'un objet non texturé vous passez à l'objet non texturé. Comme ceci:

//render first buffer with texture 
commandEncoder.setRenderPipelineState(texturedRPS) // Set the textured state 
commandEncoder.setVertexBuffer(self.vertexBuffer1, offset: 0, at: 0) 
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1) 
commandEncoder.setFragmentTexture(self.texture, at: 0) 
commandEncoder.setFragmentSamplerState(self.samplerState, at: 0) 
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count1, instanceCount: 1) 

//render second buffer without texture 
commandEncoder.setRenderPipelineState(untexturedRPS) // Set the untextured state 
commandEncoder.setVertexBuffer(self.vertexBuffer2, offset: 0, at: 0) 
commandEncoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 1) 
// No need to set the fragment texture as we don't need it in the fragment function. 
// commandEncoder.setFragmentTexture(nil, at: 0) 
commandEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: count2, instanceCount: 1) 

Aucun changement n'est requis pour la fonction vertex. Alors que vous devez diviser la fonction de fragment en deux:

fragment float4 fragment_func_textured(Vertex vert [[stage_in]], 
             texture2d<float> tex2D  [[ texture(0) ]], 
             sampler   sampler2D [[ sampler(0) ]]) { 
    //texture color 
    return tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1])); 
} 

fragment float4 fragment_func_untextured(Vertex vert [[stage_in]]) { 
    //color color 
    return vert.color; 
} 

Vous pouvez même aller de l'avant et ont deux fonctions de vertex différents que la sortie deux structures de sommets différents afin d'économiser quelques octets. En fait, la fonction de fragment texturé n'a besoin que du champ texCoord et non du color, tandis que la fonction non texturée est l'inverse.

EDIT: Vous pouvez utiliser cette fonction de fragment utiliser à la fois la couleur de la texture et la couleur du sommet:

fragment float4 fragment_func_blended(Vertex vert [[stage_in]], 
             texture2d<float> tex2D  [[ texture(0) ]], 
             sampler   sampler2D [[ sampler(0) ]]) { 
    // texture color 
    float4 texture_sample = tex2D.sample(sampler2D, float2(vert.texCoord[0],vert.texCoord[1])); 
    // vertex color 
    float4 vertex_sample = vert.color; 
    // Blend the two together 
    float4 blended = texture_sample * vertex_sample; 

    // Or use another blending operation. 
    // float4 blended = mix(texture_sample, vertex_sample, mix_factor); 
    // Where mix_factor is in the range 0.0 to 1.0. 
    return blended; 

} 
+0

C'est tellement plus propre, merci :) Une façon d'obtenir l'effet de multiplication? Si je pouvais le faire à l'intérieur du shader, ce serait facile - il suffit de multiplier la couleur tex et la couleur de couleur – jonydep

+0

Bien sûr, vous pouvez! Ensuite, utilisez simplement une autre fonction de fragment comme ceci: – ThreeDeeZero

+0

Je vais juste éditer la réponse précédente pour montrer la nouvelle fonction de fragment. – ThreeDeeZero