Skip to content

How to use the custom shader for drawing the height map(mesh) in threepp #304

@asmwarrior

Description

@asmwarrior

Hi, here is class I used to draw a mesh

// =============================================================
// Helper Functions (outside the class)
// =============================================================

// Example height function
float f(float x, float y, float tx = 0.0f)
{
    float dx = x - 0.5f;
    float dy = y - 0.5f;
    float r = std::sqrt(dx * dx + dy * dy);
    return std::sin((r + tx) * 8.0f * 3.1415926f) * 0.5f;
}

// Generate grid like your old code
void generate_grid(int N, std::vector<threepp::Vector3>& vertices, std::vector<unsigned int>& indices)
{
    vertices.clear();
    indices.clear();

    for (int j = 0; j <= N; ++j)
    {
        for (int i = 0; i <= N; ++i)
        {
            float x = (float)i / (float)N;
            float y = (float)j / (float)N;
            float z = f(x, y);
            vertices.push_back({x, y, z});
        }
    }

    for (int j = 0; j < N; ++j)
    {
        for (int i = 0; i < N; ++i)
        {
            int row1 = j * (N + 1);
            int row2 = (j + 1) * (N + 1);

            // triangle 1
            indices.push_back(row1 + i);
            indices.push_back(row1 + i + 1);
            indices.push_back(row2 + i + 1);

            // triangle 2
            indices.push_back(row1 + i);
            indices.push_back(row2 + i + 1);
            indices.push_back(row2 + i);
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

#define TEST 1

#if not TEST

class SurfaceRenderer
{
public:
    SurfaceRenderer()
    {
        m_Geometry = std::make_shared<threepp::BufferGeometry>();
        m_Material = threepp::ShaderMaterial::create();

        // Use the simplified shaders below
        m_Material->vertexShader = m_VertexShader;
        m_Material->fragmentShader = m_FragmentShader;

        // No need for ZL/ZH uniforms in the simplified version
        m_Material->uniforms = {};

        m_Mesh = std::make_shared<threepp::Mesh>(m_Geometry, m_Material);
        m_Mesh->frustumCulled = false;
    }

    void SetData(const std::vector<threepp::Vector3>& vertices,
                 const std::vector<unsigned int>& indices)
    {
        std::vector<float> verticesData;
        verticesData.reserve(vertices.size() * 3);
        for (const auto& v : vertices)
        {
            verticesData.push_back(v.x);
            verticesData.push_back(v.y);
            verticesData.push_back(v.z);
        }

        auto positionsUnique = threepp::TypedBufferAttribute<float>::create(verticesData, 3);
        m_Geometry->setAttribute("position", std::move(positionsUnique));
        m_Geometry->setIndex(indices);
        m_Geometry->computeVertexNormals();
        m_Geometry->computeBoundingSphere();
        m_Geometry->computeBoundingBox();
    }

    void SetZRange(float /*zl*/, float /*zh*/) {}

    std::shared_ptr<threepp::Mesh> GetMesh()
    {
        return m_Mesh;
    }

private:
    std::shared_ptr<threepp::BufferGeometry> m_Geometry;
    std::shared_ptr<threepp::ShaderMaterial> m_Material;
    std::shared_ptr<threepp::Mesh> m_Mesh;

    const std::string m_VertexShader = R"(#version 330 core
layout(location = 0) in vec3 vertPos;
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;

out vec3 pos;

void main()
{
    gl_Position = projectionMatrix * modelViewMatrix * vec4(vertPos, 1.0);
    pos = vertPos;
})";

    const std::string m_FragmentShader = R"(#version 330 core
uniform float ZL;
uniform float ZH;

in vec3 pos;
out vec3 color;

vec3 jet(float t)
{
    return clamp(vec3(1.5) - abs(4.0 * vec3(t) + vec3(-3, -2, -1)),
                 vec3(0), vec3(1));
}

void main()
{
    float param = (pos.z - ZL) / (ZH - ZL);
    color = jet(param);
})";
};

#else


// =============================================================
// TEST MODE: MeshBasicMaterial-based Renderer
// =============================================================
class SurfaceRenderer
{
public:

    SurfaceRenderer()
    {
        m_Geometry = std::make_shared<threepp::BufferGeometry>();
        m_Material = threepp::MeshBasicMaterial::create();
        m_Material->color = threepp::Color::red;
        m_Mesh = std::make_shared<threepp::Mesh>(m_Geometry, m_Material);
    }

    void SetData(const std::vector<threepp::Vector3>& vertices,
                 const std::vector<unsigned int>& indices)
    {
        std::vector<float> verticesData;
        verticesData.reserve(vertices.size() * 3);
        for (auto& v : vertices)
        {
            verticesData.push_back(v.x);
            verticesData.push_back(v.y);
            verticesData.push_back(v.z);
        }

        auto positionsUnique = threepp::TypedBufferAttribute<float>::create(verticesData, 3);
        m_Geometry->setAttribute("position", std::move(positionsUnique));
        m_Geometry->setIndex(indices);
        m_Geometry->computeVertexNormals();
        m_Geometry->computeBoundingSphere();
        m_Geometry->computeBoundingBox();
    }

    void SetZRange(float /*zl*/, float /*zh*/)
    {
        // ignored for testing
    }

    std::shared_ptr<threepp::Mesh> GetMesh()
    {
        return m_Mesh;
    }

private:

    std::shared_ptr<threepp::BufferGeometry> m_Geometry;
    std::shared_ptr<threepp::MeshBasicMaterial> m_Material;
    std::shared_ptr<threepp::Mesh> m_Mesh;
    const std::string m_VertexShader = "";
    const std::string m_FragmentShader = "";
};


#endif

Here is the client code to use this class:

        // Create and keep the instance alive
        surface = std::make_shared<SurfaceRenderer>();

        std::vector<threepp::Vector3> vertices1;
        std::vector<unsigned int> indices1;
        generate_grid(50, vertices1, indices1);

        surface->SetData(vertices1, indices1);

        float zl = +std::numeric_limits<float>::max();
        float zh = -std::numeric_limits<float>::max();
        for (auto &v: vertices1)
        {
            if(v.z < zl) zl = v.z;
            if(v.z > zh) zh = v.z;
        }
        surface->SetZRange(zl, zh);

        scene->add(surface->GetMesh());

You can see that if the TEST is defined as 1, then the simplified red mesh is shown, see the image shot below:

Image

But if the TEST is defined as 0, nothing is shown. I mean I can't use the ShaderMaterial class for customized shaders.

The expected output is like below, it is a kind of height map(jet color map), which means the higher parts of the mesh will shown in red, and the lower parts of the mesh will shown in blue, see image shot below:

Image

The above image is drawn by pure OpenGL code/render, I'm trying to migrate the code from the pure OpenGL to threepp framework, but found a bit hard. Can you help? Thanks.

BTW, It looks like the mouse rotation method has slightly different along different x,y,z axis. I mean in some axis' rotation, it is hard to rotate. Is the mouse rotation follow the way like: Object Mouse Trackball - OpenGL Wiki?

In my own pure OpenGL code, I don't see the mouse rotation is different if I rotate on different direction. But under threepp, it looks like in some axis, the rotation angle has a positive and negative limit? Thanks.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions