Sombreadores padrão GLSL

7

Venho armando meu mecanismo com código de verificação de erro. Vou tentar descrever essa situação com minhas melhores habilidades. Sempre que carrego um sombreador e há um erro (arquivo não existe, erro de compilação, qualquer coisa que possa dar errado) eu excluo o sombreador e defino o ponteiro como 0 (não consigo pensar em outra maneira de garantir que não tente anexar ou excluir lixo por acidente). Não quero interromper o programa se isso acontecer, porque alguém pode excluir ou modificar um único arquivo por acidente na pasta do jogo e não deseja tornar o jogo inteiro inutilizável por causa disso.

Agora, o problema é que existe um comportamento padrão na situação em que anexo shaders a um programa que tem o ponteiro igual a 0?

No meu programa, se eu anexar um shader de vértice igual a 0 e um shader de fragmento igual a 0, o OpenGL usará o programa padrão (exibirá tudo em branco):

insira a descrição da imagem aqui

Este é um cubo girado cujas dimensões já estão no espaço do clipe, renderizadas sem projeção em perspectiva ou verificação de profundidade. É carregado de um arquivo .obj.

Agora, se eu anexar um shader de fragmento adequado, mas o shader de vértice será 0, recebo o seguinte:

insira a descrição da imagem aqui

Qual é o que eu esperaria, tudo que o shader de fragmento faz é gerar um vec4 (1.0, 0.3, 0.7, 1.0), não há shader de vértice, então o OpenGL usa o padrão (essa é uma suposição correta?).

Agora, no entanto, se eu anexar um shader de vértice correto, mas anexar um shader de fragmento igual a 0 (ou não anexarei nenhum shader de fragmento), vou receber essa bagunça:

insira a descrição da imagem aqui

Algo que você não pode ver é que esse modelo está sendo pintado de cores diferentes, então, na minha tela, parece uma viagem ácida com linhas horizontais pretas.

Estou um pouco confuso, porque eu esperaria que o cubo fosse apenas branco. Gostaria de ressaltar que meu código de sombreador é simples, não estou trocando nenhuma variável entre sombreadores de vértice e fragmentos. Verificando se o ponteiro não é igual a 0 não muda nada, o programa vinculado age da mesma forma que se eu desse um ponteiro igual a 0. O mais estranho para mim é que ele sempre está bem sombreado, não é apenas lixo.

EDIT: Talvez o shader de fragmento padrão espera um valor gl_ do vértice que eu estou omitindo para definir? Isso meio que explica por que a superfície é bem sombreada, o sombreador de fragmento espera uma entrada suave, mas esse valor é apenas lixo, porque eu não o defini.

Agora, esse é um "comportamento indefinido" ou é assim que é suposto funcionar?

dreta
fonte
Sinto que notar que querer se proteger contra "alguém pode excluir um arquivo" não é super produtivo. Eles têm a mesma probabilidade de excluir seu .exe, substituir um arquivo por um totalmente incompatível ou assim por diante. Os sombreadores, materiais, malhas e outros itens padrão são úteis para depuração e teste, e você os deseja absolutamente, mas não se preocupe muito em fazer as coisas funcionarem perfeitamente quando os dados necessários foram destruídos por usuários finais tolos.
Sean Middleditch
@seanmiddleditch Essa é uma maneira de ver as coisas. Eu acho que posso mover o código de verificação de erro para #ifdef _DEBUG?
Dreta 10/05
Meu ponto era mais não escrever toneladas de códigos sofisticados de autopreservação, não que esse código devesse estar apenas no modo de depuração. Há uma grande série de 3 porções de artigos sobre o blog bitsquid.se abou manipulação de erros em jogos, bitsquid.blogspot.com/2012/01/...
Sean Middleditch

Respostas:

5

Verifique a seção 2.11 da especificação OpenGL 4.2 :

O código executável para um estágio de shader individual é obtido do programa atual para esse estágio. Se houver um objeto de programa atual estabelecido pelo UseProgram, esse programa será considerado atual para todos os estágios. Caso contrário, se houver um objeto de pipeline de programa vinculado (consulte a seção 2.11.4), o programa vinculado ao estágio apropriado do objeto de pipeline será considerado atual. Se não houver nenhum objeto de programa atual ou objeto de pipeline de programa vinculado, nenhum programa estará atualizado para qualquer estágio. O programa atual para um estágio é considerado ativo se contiver código executável para esse estágio; caso contrário, nenhum programa é considerado ativo para esse estágio. Se não houver um programa ativo para os estágios do vértice ou do fragment shader, os resultados do processamento do vértice e / ou fragmento serão indefinidos.No entanto, isso não é um erro. Se não houver um programa ativo para os estágios de controle de mosaico, avaliação de mosaico ou sombreador de geometria, esses estágios serão ignorados.

Esse é um "comportamento indefinido" que age de maneira diferente por implementação.

Quanto à solução para não conseguir encontrar um sombreador, achei melhor codificar um sombreador de vértice e um fragmento e carregá-los antes de qualquer outra coisa e usá-lo no lugar de qualquer sombreador ausente. Dessa forma, muitos dos uniformes que eu defino (matrizes de transformação) ainda serão aprovados e o resultado parecerá meio decente. Por exemplo, aqui está como eu faço isso (C #):

string defaultVertShaderText = 
    @"#version 120

    attribute vec4 in_vertex;

    uniform mat4 model;
    uniform mat4 view;
    uniform mat4 projection;

    void main()
    {
        gl_Position = projection * view * model * in_vertex;
    }";

string defaultFragShaderText =
    @"#version 120

    void main()
    {
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }";

Também codificar uma textura padrão é uma boa idéia.

Robert Rouhani
fonte
Obrigado pela resposta. E eu estava pensando da mesma maneira para lidar com sombreadores vazios, só que eu faço isso no nível do gerente de programa (se o programa estiver errado, eu uso um programa padrão), então tudo o que preciso fazer é usar meus sombreadores padrão no programa fase de compilação e vai funcionar, sim. Obrigado novamente.
Dreta
@Robert: -1: Por favor, não passe matrizes de strings assim para o OpenGL. O glShaderSource usa matrizes, mas não é assim que essa funcionalidade deve ser usada. Cada sequência não pretende ser uma linha separada.
Nicol Bolas
@ NicolBolas Mencionei que é assim que estou fazendo no meu jogo em C #. O OpenTK fornece uma sobrecarga de chamadas GL regulares para interagir com tipos de C # da maneira que você naturalmente esperaria. Tanto quanto eu testei (ATI, AMD, NVIDIA, Intel, Apple e Mesa entre Windows, Linux e OS X), os shaders compilam, vinculam e operam corretamente e sem erros. Eu acho que se você preferir, eu poderia remover "aqui está como eu faço isso (C #)" e substituí-lo pelo C sytnax pela mesma coisa.
Robert Rouhani
@RobertRouhani: Isso não tem nada a ver com a "sintaxe C". É sobre o fato de você estar construindo uma matriz. Deve ser uma única sequência , não uma matriz de sequências (ou uma matriz de uma sequência). Sim, ele "compila, vincula e opera corretamente e sem erros". Mas ainda é a maneira errada de usar o fato de que é glShaderSourcepreciso um conjunto de strings.
Nicol Bolas
2
@RobertRouhani: Está errado, porque não é para isso que serve o array. É necessário um conjunto de cadeias de caracteres para que você possa efetivamente criar um sombreador compilado a partir de peças que se referem umas às outras. E para que você possa montar prefixos de cabeçalho para shaders, para que eles possam trabalhar com versões diferentes usando #ifdef. É não há de modo que você pode enviar cada linha como uma string separada. Sim, funciona, mas é um estilo de codificação horrível. É como criar um arquivo .cpp #incluindo cada linha individualmente . Sim, ele será compilado, mas não é assim que você usa #include.
Nicol Bolas