Cycles shaders render time test

Hi, I have been making some simple tests to see how different shaders in Cycles are influencing render times and though I didn’t reach groundbreaking conclusions I did come upon an unexpected result: the diffuse shader renders slower than the glossy one. At least in my test. Did anyone stumble upon this, what would the explanation be? I am positive that in BI, reflective materials add lots to the render time and I was still avoiding glossy in Cycles when possible, but this could make me add some gloss to my walls :slight_smile:

This is what I came up with:



Glossy blurred (roughness 1) does render a bit slower than sharp glossy as I expected.

I started testing this because I’m putting together some tips on ways to decrease render times for RenderStreet online render farm’s blog. I couldn’t find much documentation on the shaders effect on performance. I used a simple test scene so I can do this fast but I will try it later on a larger scene.

Most Cycles path tracer work is triangle/ray intersection test, and MAYBE complex shader evaluation. I think that glossy Beckmann is one of that have many trigonometric functions, maybe it most computational intensive, but that reading code source, not testing. Diffusion with roughness=0 is really cheap, actually it is dot product of 2 vectors.

You maybe better care about bounce number and resolution, not shaders.

Thanks for the reply, I’m not a programmer so I was thinking according my experience with Bi materials. I also use the settings in the render panel to optimise the render times but I was really sure materials count too and I was shy to add too much gloss and transparency into a scene. It seems I was wrong. Glass and translucent seem a bit more heavy but I don’t use them so much. I’ll look some more into this and post after I make a larger test. I also have to try some mixed shaders.

in short, that is diffusion shader (actually, next bounce direction sampler, not shader but who care :p) code intern/cycles/kernel/closure/bsdf_diffuse.h

__device int bsdf_diffuse_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
        float3 N = sc->N;

        // distribution over the hemisphere
        sample_cos_hemisphere(N, randu, randv, omega_in, pdf);

        if(dot(Ng, *omega_in) > 0.0f) {
                *eval = make_float3(*pdf, *pdf, *pdf);
#ifdef __RAY_DIFFERENTIALS__
                // TODO: find a better approximation for the diffuse bounce
                *domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx;
                *domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy;
#endif
        }
        else
                *pdf = 0.0f;

        return LABEL_REFLECT|LABEL_DIFFUSE;
}

Compare it to glossy intern/cycles/kernel/closure/bsdf_microfacet.h

__device int bsdf_microfacet_beckmann_sample(const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, float3 *eval, float3 *omega_in, float3 *domega_in_dx, float3 *domega_in_dy, float *pdf)
{
    float m_ab = sc->data0;
    int m_refractive = sc->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID;
    float3 N = sc->N;

    float cosNO = dot(N, I);
    if(cosNO > 0) {
        float3 X, Y, Z = N;
        make_orthonormals(Z, &X, &Y);
        // generate a random microfacet normal m
        // eq. 35,36:
        // we take advantage of cos(atan(x)) == 1/sqrt(1+x^2)
        //tttt  and sin(atan(x)) == x/sqrt(1+x^2)
        float alpha2 = m_ab * m_ab;
        float tanThetaM = safe_sqrtf(-alpha2 * logf(1 - randu));
        float cosThetaM = 1 / safe_sqrtf(1 + tanThetaM * tanThetaM);
        float sinThetaM = cosThetaM * tanThetaM;
        float phiM = 2 * M_PI_F * randv;
        float3 m = (cosf(phiM) * sinThetaM) * X +
                 (sinf(phiM) * sinThetaM) * Y +
                               cosThetaM  * Z;

        if(!m_refractive) {
            float cosMO = dot(m, I);
            if(cosMO > 0) {
                // eq. 39 - compute actual reflected direction
                *omega_in = 2 * cosMO * m - I;
                if(dot(Ng, *omega_in) > 0) {
                    // microfacet normal is visible to this ray
                    // eq. 25
                    float cosThetaM2 = cosThetaM * cosThetaM;
                    float tanThetaM2 = tanThetaM * tanThetaM;
                    float cosThetaM4 = cosThetaM2 * cosThetaM2;
                    float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 *  cosThetaM4);
                    // eq. 24
                    float pm = D * cosThetaM;
                    // convert into pdf of the sampled direction
                    // eq. 38 - but see also:
                    // eq. 17 in http://www.graphics.cornell.edu/~bjw/wardnotes.pdf
                    *pdf = pm * 0.25f / cosMO;
                    // Eval BRDF*cosNI
                    float cosNI = dot(N, *omega_in);
                    // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
                    float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
                    float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
                    float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
                    float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
                    float G = G1o * G1i;
                    // eq. 20: (F*G*D)/(4*in*on)
                    float out = (G * D) * 0.25f / cosNO;
                    *eval = make_float3(out, out, out);
#ifdef __RAY_DIFFERENTIALS__
                    *domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx;
                    *domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy;
#endif
                }
            }
        }
        else {
            // CAUTION: the i and o variables are inverted relative to the paper
            // eq. 39 - compute actual refractive direction
            float3 R, T;
#ifdef __RAY_DIFFERENTIALS__
            float3 dRdx, dRdy, dTdx, dTdy;
#endif
            float m_eta = sc->data1;
            bool inside;
            fresnel_dielectric(m_eta, m, I, &R, &T,
#ifdef __RAY_DIFFERENTIALS__
                dIdx, dIdy, &dRdx, &dRdy, &dTdx, &dTdy,
#endif
                &inside);

            if(!inside) {
                *omega_in = T;
#ifdef __RAY_DIFFERENTIALS__
                *domega_in_dx = dTdx;
                *domega_in_dy = dTdy;
#endif

                // eq. 33
                float cosThetaM2 = cosThetaM * cosThetaM;
                float tanThetaM2 = tanThetaM * tanThetaM;
                float cosThetaM4 = cosThetaM2 * cosThetaM2;
                float D = expf(-tanThetaM2 / alpha2) / (M_PI_F * alpha2 *  cosThetaM4);
                // eq. 24
                float pm = D * cosThetaM;
                // eval BRDF*cosNI
                float cosNI = dot(N, *omega_in);
                // eq. 26, 27: now calculate G1(i,m) and G1(o,m)
                float ao = 1 / (m_ab * safe_sqrtf((1 - cosNO * cosNO) / (cosNO * cosNO)));
                float ai = 1 / (m_ab * safe_sqrtf((1 - cosNI * cosNI) / (cosNI * cosNI)));
                float G1o = ao < 1.6f ? (3.535f * ao + 2.181f * ao * ao) / (1 + 2.276f * ao + 2.577f * ao * ao) : 1.0f;
                float G1i = ai < 1.6f ? (3.535f * ai + 2.181f * ai * ai) / (1 + 2.276f * ai + 2.577f * ai * ai) : 1.0f;
                float G = G1o * G1i;
                // eq. 21
                float cosHI = dot(m, *omega_in);
                float cosHO = dot(m, I);
                float Ht2 = m_eta * cosHI + cosHO;
                Ht2 *= Ht2;
                float out = (fabsf(cosHI * cosHO) * (m_eta * m_eta) * (G * D)) / (cosNO * Ht2);
                // eq. 38 and eq. 17
                *pdf = pm * (m_eta * m_eta) * fabsf(cosHI) / Ht2;
                *eval = make_float3(out, out, out);
            }
        }
    }
    return (m_refractive) ? LABEL_TRANSMIT|LABEL_GLOSSY : LABEL_REFLECT|LABEL_GLOSSY;
}



Thanks for posting this!
I have endless admiration for those who are creating this kind of software. I am the geek on the user end who is happy when the shiny nice image fills the screen :slight_smile: But I have been using rendering software for more than 15 years so I know programming that is done today grew from a lot of work from a lot of people…

Baack to the subject at hand, even not knowing much about code, I can see the glossy code has more in it than the diffuse.

Meanwhile, I redid my tests and got the same result: faster render time on the glossy material than the diffuse. Only I found what the catch was, the glossy surfaces introduce … noise… and fireflies… lots of them… doh! that were almost zero in the diffuse test (it was not showing in the simple test scene but was a catastrophe on a large interior scene). So you’ll have to increase samples and get a longer render time for glossy. This brought me to the noise issue so I started testing, found some more information on that and wrote my blog post accordingly.

Blog post

The thing that strikes me as most important is getting a good balance between light intensity in the scene and intensity of the material colours. Increasing the light + darker materials over using dim light and bright materials. Do you think I got this right??

I think I’m starting to get a better grasp on Cycles and I really need it as I am starting on a new project and hope I won’t get stuck in technical issues. I still got a lot of experimenting to do.