Bridge Examples:

Java
Java
C#
C#
PHP
PHP
Python
Python
C++
C++
TypeScript
TypeScript
▸ Bridge Quick Review

Creating a 3D Model and Shader System in C++

One of the easiest ways to give your video game a graphical uplift is by adding in some cool shaders. Shaders essentially change the appearance of a 3D model when it's drawn on the screen, and can make it a solid color, look like glass, or even glow! This only scratches the surface of the possibilities, too.

Using the Bridge pattern allows a 3D model to use any shader available, creating an exponential explosion of combinations and possibilities.

We won't be going over actual shader work in this demo, since it would be a little outside the scope of the lesson, but we can take a look at how such a system would be structured to allow maximum flexibility.

Shader Implementor:

class Shader
{
   public:
       virtual ~Shader() {};
       virtual void Render(Model* model) = 0;
};

Model Abstraction:

class Model
{
   public:
       Model(Shader* shader)
       : pShader(shader) 
       {};
       virtual ~Model() {};
       void SetShader(Shader* shader) { this->pShader = shader; }

       // Abstract Methods:
       virtual void Draw() = 0;
       virtual std::string GetName() = 0;

   protected:
       Shader* pShader;
};

Shader Examples:

// Shader example 1: unlit color shader:
class UnlitColorShader: public Shader
{
   public:
       UnlitColorShader(std::string color)
       : Shader(), color(color)
       {};
       ~UnlitColorShader() {};

       void Render(Model* model) override;
   protected:
       std::string color;
};

void UnlitColorShader::Render(Model* model)
{
   std::cout << "Rendering " << model->GetName() << " with a "
   << this->color << " color." << std::endl;
}

// Shader example 2: glass/transparent shader:
class GlassShader: public Shader
{
   public:
       GlassShader()
       : Shader()
       {};
       ~GlassShader() {};

       void Render(Model* model) override;
};

void GlassShader::Render(Model* model)
{
   std::cout << "Rendering " << model->GetName()
   << " to Look like glass." << std::endl;
}

Model Examples:

// Model example 1: a cube:
class CubeModel: public Model
{
   public:
       CubeModel(Shader* shader)
       : Model(shader)
       {};
       ~CubeModel() {};

       void Draw() override
       {
           this->pShader->Render(this);
       }

       std::string GetName() override { return "Cube"; }
};

// Model example 2: a monkey:
class Suzanne: public Model
{
   public:
       Suzanne(Shader* shader)
       : Model(shader)
       {};
       ~Suzanne() {};

       void Draw() override
       {
           this->pShader->Render(this);
       }

       std::string GetName() override { return "Suzanne"; }
};

Demo of the Bridge Pattern:

int main()
{
   // Create shaders:
   Shader* solidRed = new UnlitColorShader("red");
   Shader* solidBlue = new UnlitColorShader("blue");
   Shader* glass = new GlassShader();

   // Create the models:
   Model* cube = new CubeModel(solidRed);
   Model* monkey = new Suzanne(glass);

   // Draw the two models:
   cube->Draw(); // "Rendering Cube with a red color."
   monkey->Draw(); // "Rendering Suzanne to look like glass."

   // Switch out the shader on suzanne:
   monkey->SetShader(solidBlue);

   monkey->Draw(); // "Rendering Suzanne with a blue color."

   // Clean up:
   delete cube;
   delete monkey;
   
   delete glass;
   delete solidBlue;
   delete solidRed;
}

Find any bugs in the code? let us know!