Shader de alto rendimiento basado en Blinn-Phong, que admite sombras y reflejos ambientales

Introducción: Sun Ermiao, un miembro prolífico de la comunidad, hoy le ofrece un conjunto completo de modelos de iluminación tradicionales Shader, que integra las capacidades de iluminación, sombras y reflexión ambiental de Cocos Creator, lo que le permite intercambiar libremente entre efectos de renderizado y rendimiento.

El texto comienza

La función de renderizado 3D del motor Cocos Creator ha admitido procesos de renderizado modernos estándar desde el principio, como materiales PBR, renderizado HDR, etc. Pero para algunas plataformas con potencia informática estricta o control estricto sobre el consumo de calor y energía, necesitamos algunos métodos de renderizado de bajo consumo para reducir la sobrecarga de renderizado. Con base en este requisito, reescribí un Cocos Shader de alto rendimiento basado en el modelo de iluminación tradicional y pude aprovechar las capacidades integradas de iluminación, sombras y reflexión ambiental de Cocos Creator. Espero que pueda ayudar a los desarrolladores que lo necesiten.

  • Dirección de la experiencia:

http://learncocos.com/light

  • Dirección del código fuente:

https://store.cocos.com/app/detail/5256

En gráficos por computadora, el modelo de iluminación se utiliza para simular el efecto de reflexión de la superficie de un objeto bajo iluminación luminosa. Este artículo utiliza Cocos Creator para demostrar los efectos y códigos de muestra de los modelos de iluminación de uso común (usando el lenguaje GLSL).

3e410f31d853d173b40e41a86bf95cde.png


Apagado (sin luz)

El modelo sin iluminación no considera el impacto de la iluminación y solo representa el color o la textura del objeto directamente en la pantalla. Este modelo es adecuado para escenas que no requieren efectos de iluminación, como efectos especiales como vallas publicitarias o guía terrestre.

fce2c9e8f99c859923b28157ff207bdd.png

El código de implementación es el siguiente:

void main()
{
    vec4 o = mainColor; //材质颜色
    return CCFragOutput(o);
}

Lambert(Lamberto)

El modelo de iluminación de Lambert es un modelo de iluminación que describe la reflexión difusa, que supone que la reflexión de la luz desde la superficie de un objeto no depende de la posición del observador. Este modelo se utiliza a menudo para simular superficies de objetos no metálicos y no especulares.

61874653fdf22a6a056b1b1025770723.png

El código de implementación es el siguiente:

void Lambert(inout vec4 diffuseColor,in vec3 normal)
{
    vec3 N = normalize(normal);
    vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
    float NL = max(dot(N, L), 0.0);
    vec3 diffuse = NL * (diffuseColor.rgb * cc_mainLitColor.xyz);
    vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
    diffuseColor.rgb = ambient + diffuse;
}

Medio Lambert(medio Lambert)

El modelo de iluminación semi-lambertiano es una variante del modelo de iluminación de Lambert, que cambia la interpretación de la reflexión de la luz de modo que cuando la luz está en un ángulo de 90 grados con respecto a la normal, la intensidad de la reflexión es 0,5 en lugar de 0, lo que hace que el parte de la sombra No es tan oscuro, aquí hemos realizado algunas optimizaciones y agregado los parámetros de difusaWrap,  pow(NL * diffuseWrap + (1.-diffuseWrap),2.0) en lugar de  usar pow(NL *0.5 +00.5,2.). Este modelo de iluminación se utiliza a menudo para dibujos animados o renderizados no fotorrealistas.

b119c332950ad8f23a8fd87c595beb0f.png

En comparación con el modelo Lambert, la parte de sombra del modelo medio Lambert no es tan oscura (lado izquierdo de la imagen a continuación). También podemos usar difusaWrap para controlar la intensidad de la sombra de la parte oscura.

976b14b11017195147d2800974727ad6.png

El código de implementación es el siguiente:

void HalfLambert(inout vec4 diffuseColor,in vec3 normal)
{
    vec3 N = normalize(normal);
    vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
    float NL = max(dot(N, L), 0.0);
    vec3 diffuse = pow(NL * diffuseWrap + (1.-diffuseWrap),2.0) * (diffuseColor.rgb * cc_mainLitColor.xyz);
    vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
    diffuseColor.rgb = ambient + diffuse;
}

Blinn-Phong(Blinn-Feng)

El modelo de iluminación Blinn-Phong es una versión mejorada del modelo de iluminación Phong e introduce el concepto de "medio vector" para hacer más eficiente el cálculo de las luces especulares. Adecuado para simular superficies brillantes.

96900efd97ce84dd0f6960b723205ad9.png

El código de implementación es el siguiente:

void void blinnPhong(inout vec4 diffuseColor,in vec3 normal)
{
    vec3 N = normalize(normal);
    vec3 L = normalize(cc_mainLitDir.xyz * -1.0);
    float NL = max(dot(N, L), 0.0);
    vec3 diffuse = NL * diffuseColor.rgb * cc_mainLitColor.xyz;
    vec3 position;
    HIGHP_VALUE_FROM_STRUCT_DEFINED(position, v_position);
    vec3 cameraPosition = cc_cameraPos.xyz / cc_cameraPos.w;
    vec3 V = normalize(cameraPosition- position);
    vec3 H = normalize(L + V);
    float specularFactor = pow(max(0.0, dot(H,N)), bpParams.x*50.);
    vec3 specular = (specularFactor * cc_ambientSky.rgb * cc_mainLitColor.xyz);
    float shadowCtrl = 1.0;
    #if CC_RECEIVE_SHADOW && CC_SHADOW_TYPE == CC_SHADOW_MAP
      if (NL > 0.0) {
      #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_CASCADED
        shadowCtrl = CCCSMFactorBase(position, N, v_shadowBias);
      #endif
      #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_UNIFORM
        shadowCtrl = CCShadowFactorBase(CC_SHADOW_POSITION, N, v_shadowBias);
      #endif
      }
    #endif
    diffuse = (diffuse + specular) * (shadowCtrl);
}

Toon (colorear dibujos animados)

El modelo de dibujos animados, o Cel-Shading, simula el efecto de una animación dibujada a mano discretizando la cantidad de intensidad de la luz en varios niveles. Adecuado para dibujos animados o renderizado de estilo artístico.

952ab6395ffa71f424e33bb7b8c74845.png

El código de implementación es el siguiente:

void ToonShading (inout vec4 diffuseColor,in vec3 normal) {
    vec3 position;
    HIGHP_VALUE_FROM_STRUCT_DEFINED(position, v_position);
    vec3 V = normalize(cc_cameraPos.xyz - position);
    vec3 N = normalize(normal);
    vec3 L = normalize(-cc_mainLitDir.xyz);
    float NL = 0.5 * dot(N, L) + 0.5;
    float NH = 0.5 * dot(normalize(V + L), N) + 0.5;
    vec3 lightColor = cc_mainLitColor.rgb * (cc_mainLitColor.w * shadeParams.x);
    float shadeFeather = shadeParams.y;
    float shadeCtrl = mix(1., (1.-shadeParams.z), clamp(1.0 + (shadeParams.x - shadeFeather - NL) / shadeFeather, 0.0, 1.0));
    shadeCtrl *= mix(1., (1.-shadeParams.z*0.5), clamp(1.0 + (shadeParams.w - shadeFeather - NL) / shadeFeather, 0.0, 1.0));
    float specularWeight = 1.0 - pow(specularParams.x, 5.0);
    float specularMask = 1.0-smoothstep( NH, NH+ specularParams.y, specularWeight + EPSILON_LOWP);
    float shadowCtrl = 1.0;
    #if CC_RECEIVE_SHADOW && CC_SHADOW_TYPE == CC_SHADOW_MAP
      if (NL > 0.0) {
      #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_CASCADED
        shadowCtrl = CCCSMFactorBase(position, N, v_shadowBias+0.1);
      #endif
      #if CC_DIR_LIGHT_SHADOW_TYPE == CC_DIR_LIGHT_SHADOW_UNIFORM
        shadowCtrl = CCShadowFactorBase(CC_SHADOW_POSITION, N, v_shadowBias+0.1);
      #endif
      }
    #endif
    float diffuseCtrl = (shadowCtrl+specularMask*specularParams.z)*shadeCtrl;
    vec3 envColor = cc_ambientGround.rgb*cc_ambientSky.w;
    diffuseColor.rgb *= (envColor + (lightColor*diffuseCtrl));
  }

También podemos lograr diferentes estilos de renderizado estilizados a través de Rim Light.

01b687445b83cf2c315e8259f57fdde2.png

El código de implementación es el siguiente:

#if USE_RIM_LIGHT
        float fRim = (1.0 - dot(v_view_normal,vec3(0,0,1.0))) * rimColor.w;
        color.rgb = mix(color.rgb,rimColor.rgb,fRim);
    #endif

PBR vs. Blinn-Phong

PBR (Representación basada físicamente) es el último modelo de iluminación, que intenta simular de manera más realista la interacción de la luz y las superficies de los objetos, incluida la reflexión difusa y la reflexión especular. Los modelos PBR suelen incluir principios físicos como la conservación de energía y la equivalencia de Fresnel y son adecuados para simular la representación del mundo real.

PBR es el modelo de iluminación predeterminado para casi todos los motores gráficos estándar modernos, sin embargo, debido a que PBR implica demasiados cálculos de fórmulas y muestreo de texturas, su código Shader es muy complejo y requiere una alta potencia informática en el equipo del usuario. Los estudiantes interesados ​​en esta área pueden ver directamente el código fuente incorporado de Shader de la última versión del motor Cocos.

https://github.com/cocos/cocos-engine/tree/develop/editor/assets/chunks

Pero en muchos casos, podemos generar resultados aceptables sin PBR.

Como se muestra abajo:

28c69d3b5a029b23b6e59ffbf1ea465f.png

Izquierda: Blinn-Phong, derecha: PBR

Los diferentes modelos de iluminación son adecuados para diferentes estilos de renderizado y pueden seleccionarse y utilizarse según las necesidades y escenarios específicos. Por ejemplo, el modelo sin iluminación es adecuado para vallas publicitarias o guías de suelo, el modelo con iluminación Lambert es adecuado para superficies mate, el modelo de dibujos animados es adecuado para renderizado de dibujos animados o estilo dibujado a mano, y PBR es adecuado para renderizado de alta calidad que simula el mundo real.

Términos comunes

En Shader, no importa qué modelo de iluminación utilicemos, existen algunas tecnologías y términos comunes, cada uno de los cuales asume diferentes funciones y propósitos. A continuación se muestran algunas explicaciones de conceptos:

Color : suele ser un valor RGBA que representa el color básico de un píxel. R, G y B representan rojo, verde y azul respectivamente, y A representa transparencia. Estos valores suelen estar entre 0 y 1.

910da45919031db72a00213877fc8254.png

Mapa de Albedo : Un mapa de color sin ninguna información de iluminación. Representa principalmente el color inherente de la superficie del objeto y no se ve afectado por la iluminación. Generalmente se usa en modelos de iluminación PBR.

2e6cbd21a41a16d99e2e35dd0f788b26.png

Mapa difuso : mapa de reflexión difusa, que puede contener información de iluminación, como AO, sombreado, etc. Generalmente utilizado en modelos de iluminación tradicionales.

Prueba Alfa : La prueba Alfa es una técnica que determina si se debe descartar un píxel comparando su valor Alfa con un umbral preestablecido. Esta técnica se utiliza a menudo para lograr efectos de transparencia y translucidez.

El código de implementación es el siguiente:

#if USE_ALPHA_TEST
      if (color.ALPHA_TEST_CHANNEL < colorScaleAndCutoff.w) discard;
    #endif

Mapa normal : el mapeo normal es una técnica utilizada para simular detalles de la superficie. Utiliza un mapa para almacenar vectores que describen la dirección normal de la superficie en cada punto, haciendo que la superficie del objeto parezca más detallada.

60aa9861ba382ad7a50789756e2fd6b7.png

Mapa emisivo : un mapa emisivo es un mapa de textura que se utiliza para representar el color y el brillo espontáneos de un objeto en ausencia de iluminación externa.

Niebla : La niebla es una técnica utilizada para simular efectos atmosféricos que hacen que los objetos más alejados del espectador parezcan borrosos y cambien de color al color de la niebla.

Iluminación basada en imágenes  (IBL): IBL es una técnica que utiliza mapas de reflexión ambiental para simular la iluminación ambiental. Produce reflejos y efectos de iluminación más realistas.

El código de implementación es el siguiente:

#if CC_USE_IBL && USE_IBL
      vec3 cameraPosition = cc_cameraPos.xyz / cc_cameraPos.w;
      vec3 V = normalize(cameraPosition- position);
      vec3 env = vec3(1.);
      vec3 R = normalize(reflect(-V, N));
      vec3 rotationDir = RotationVecFromAxisY(R.xyz, cc_surfaceTransform.z, cc_surfaceTransform.w);
      vec4 envmap = fragTextureLod(cc_environment, rotationDir, bpParams.y * (cc_ambientGround.w - 1.0));
      #if CC_USE_IBL == IBL_RGBE
        env = unpackRGBE(envmap);
      #else
        env = SRGBToLinear(envmap.rgb);
      #endif
      diffuse = mix(env, diffuse, bpParams.x);
    #endif
    vec3 ambient = cc_ambientGround.rgb * diffuseColor.rgb * cc_ambientSky.w;
    diffuseColor.rgb = ambient + diffuse;
  }

Rim Light:  Rim Light es una tecnología que simula el efecto del borde de un objeto iluminado por luz de fondo, lo que puede aumentar la sensación tridimensional de un modelo 3D.

Principio de acción

Para las técnicas mencionadas anteriormente, los pasos generalmente se realizan en el siguiente orden en Fragment Shader:

1.  Color : Primero, necesitas saber el color básico del objeto, lo cual generalmente se logra leyendo el mapa de Albedo.

2.  Mapa normal : luego, puede aplicar Mapa normal para cambiar las normales de la superficie del objeto para simular más detalles.

3.  Cálculo de iluminación : A continuación, puede realizar el cálculo de iluminación, que generalmente incluye el cálculo de luz ambiental, luz difusa, luz especular, etc. Durante el proceso de cálculo, puede utilizar Rim Light para aumentar el brillo de los bordes.

4.  IBL : Luego, puede calcular el IBL en función de la imagen panorámica para hacer que los efectos de reflejo y sombra del entorno sean más realistas. 64010e779bd11086f61ac78823146550.png5.  Mapa Emisivo : Luego, puedes agregar un Mapa Emisivo para hacer que el objeto brille sin una fuente de luz.

6.  Prueba Alfa : Finalmente, puede realizar una prueba Alfa y decidir si descartar píxeles según los resultados de la prueba.

7.  Niebla : Una vez completados todos los cálculos de color e iluminación, puede aplicar el efecto Niebla para hacer que el color de los objetos alejados del observador pase al color de la niebla.

0caf53785cd624a9f7c05ee8f2c8d74b.png


Análisis de rendimiento

Dado que la potencia informática y el ancho de banda de la GPU de la PC son relativamente potentes, el rendimiento es casi el mismo al procesar estos modelos de iluminación. Los modelos de teléfonos móviles de gama alta no se verán muy afectados y solo unos pocos modelos de gama baja tendrán un rendimiento PBR más débil que el de Lambert.

Al mismo tiempo, observamos que el uso de sombras y trazos (contorno) duplicará el número de vértices, que es la principal razón de la degradación del rendimiento, principalmente por las dos razones siguientes:

Generación de sombras : las sombras generalmente se implementan generando mapas de sombras. Durante este proceso, la escena requiere una representación adicional desde la perspectiva de la fuente de luz. Esto significa que cada vértice debe procesarse y rasterizarse nuevamente, duplicando el número de vértices.

89211a18dcf67f2edab45738ca53295f.png

El trazo genera geometría adicional : genere una versión ligeramente más grande del modelo original y luego renderice el reverso de esta versión más grande para crear un efecto de trazo. Este enfoque duplica el número de vértices porque en realidad estás renderizando dos modelos.

b8a4cb9902875d5a6d50c6fa051303c1.png

Normalmente podemos  GPU Instancing mejorar el rendimiento del juego activándolo.

c1415f06c0e9a2d50c440f2255e4c1a9.png

Si el modelo tiene animación esquelética, se recomienda habilitar la animación horneada para  GPU Instancing su uso.

c57a3b6cd2ecd3e7359a4762d6f151f6.png

Por lo tanto, se recomienda que elija el modelo de iluminación según su estilo de juego. Según las mediciones reales, la razón principal del rendimiento débil de PBR es que IBL está encendido.

Código fuente disponible de forma gratuita

Para Blinn-Phong, también está escrita una versión simplificada de IBL en el modelo Lambert. Puede descargar el paquete de código fuente completo desde Cocos Store para obtener más información:

80778f40f546f3d86a832edd6f81223e.png

Dirección : https://store.cocos.com/app/detail/5256

Haga clic en [Leer el texto original] para saltar rápidamente, es gratis.

¡Sigue la cuenta oficial de Cocos Engine y te volverás más fuerte!

Supongo que te gusta

Origin blog.csdn.net/weixin_44053279/article/details/132506908
Recomendado
Clasificación