How to use materials without textures?


#1

Hi, I’m pretty new to bsf and I’d like to ask a simple question.

Is there a way to use the default renderer to render materials without textures, e.g. using one albedo color for the whole mesh?


#2

You can create a 1x1 texture with the color you want, and then assign it to a material.

If you wish a cleaner approach where you can specify the color directly then you’d need to create a custom surface material, as described here: https://www.bsframework.io/docs/surface_shaders.html. It’s also demoed in the CustomMaterials example.


#3

Thanks for your help. Now I’ve managed to put up a minimal shader like this

#include "$ENGINE$\PerObjectData.bslinc"

shader VertexColorShader
{
    mixin PerObjectData;

    code
    {
        struct MyVertexInput
        {
            float3 position : POSITION;
        };

        struct VStoFS
        {
            float4 position : SV_Position;
        };

        VStoFS vsmain(MyVertexInput input)
        {
            VStoFS output;

            float4 position = float4(input.position, 1.0f);
            output.position = mul(gMatWorldViewProj, position);

            return output;
        }

        float4 fsmain(in VStoFS input) : SV_Target0
        {
            return float4(1.0f, 1.0f, 1.0f, 1.0f);
        }
    };
};

It works fine when I import a mesh using gImporter()::import<Mesh>() from a file, and renders the mesh in all white. But when I try to create a mesh from scratch like below, with only positions and indices, it fails to show anything.

    std::string fmesh(argv[1]);
    std::vector<float> positions; // [x0 y0 z0 x1 y1 z1 ...]
    std::vector<unsigned> indices;
    // custom code reading a mesh file
    // ...
    const int nvertices = positions.size() / 3;
    const int nindices = indices.size();

    auto vertex_desc = bs::VertexDataDesc::create();
    vertex_desc->addVertElem(bs::VET_FLOAT3, bs::VES_POSITION);

    bs::MESH_DESC mesh_desc;
    mesh_desc.numVertices = nvertices;
    mesh_desc.numIndices = nindices;
    mesh_desc.vertexDesc = vertex_desc;
    auto mesh = bs::Mesh::create(mesh_desc);

    auto mesh_data = mesh->allocBuffer();
    auto vertex_buffer = mesh_data->getElementData(bs::VES_POSITION);
    auto stride = vertex_desc->getVertexStride();
    for (size_t i = 0; i < positions.size(); i += 3) {
        bs::Vector3 pos(positions[i], positions[i + 1], positions[i + 2]);
        memcpy(vertex_buffer, &pos, sizeof(pos));
        vertex_buffer += stride;
    }
    auto index_buffer = mesh_data->getIndices32();
    for (size_t i = 0; i < indices.size(); ++i) {
        index_buffer[i] = indices[i];
    }
    mesh->writeData(mesh_data, false);

It seems like the renderer is still expecting other vertex elements although it’s not required in the shader. So what vertex elements are expected for the renderer to work? What if I only want/have the raw geometry like above? And can I provide more vertex elements like vertex colors? Thanks in advance.


#4

Your code seems like it should work. Generally you need to convert MeshData through RendererMeshData::convert to prepare it for format that’s expected by the renderer. But it shouldn’t matter for position-only formats, as there is no conversion there.

When I get a chance I’ll take a closer look. If you can provide a working example it would be helpful in tracking down the issue sooner.


#5

Hi BearishSun, thanks for your advice. Here’s the full source code for this example, and the shader code is exactly like above.

#include <string>
#include <vector>

#include <BsApplication.h>
#include <Components/BsCCamera.h>
#include <Components/BsCRenderable.h>
#include <Importer/BsImporter.h>
#include <Material/BsMaterial.h>
#include <Material/BsShader.h>
#include <Mesh/BsMesh.h>
#include <Mesh/BsMeshData.h>
#include <RenderAPI/BsVertexDataDesc.h>
#include <Renderer/BsRendererMeshData.h>
#include <Scene/BsSceneObject.h>

#define SHADER_DIR WHERE

int main(int argc, char* argv[])
{
    // System start up
    bs::VideoMode video_mode(1280, 720);
    bs::Application::startUp(video_mode, "My App", false);

    // Read and fill in mesh data
    std::vector<float> positions{ -1.0f, 0.0f, -1.0f, -1.0f, 0.0f,
                                  1.0f,  1.0f, 0.0f,  1.0f };
    std::vector<unsigned> indices{ 0, 1, 2 };
    const int nvertices = positions.size() / 3;
    const int nindices = indices.size();

    auto vertex_desc = bs::VertexDataDesc::create();
    vertex_desc->addVertElem(bs::VET_FLOAT3, bs::VES_POSITION);

    bs::MESH_DESC mesh_desc;
    mesh_desc.numVertices = nvertices;
    mesh_desc.numIndices = nindices;
    mesh_desc.vertexDesc = vertex_desc;
    auto mesh = bs::Mesh::create(mesh_desc);

    auto mesh_data = bs::MeshData::create(nvertices, nindices, vertex_desc);
    auto vertex_buffer = mesh_data->getElementData(bs::VES_POSITION);
    auto stride = vertex_desc->getVertexStride();
    for (size_t i = 0; i < positions.size(); i += 3) {
        bs::Vector3 pos(positions[i], positions[i + 1], positions[i + 2]);
        memcpy(vertex_buffer, &pos, sizeof(pos));
        vertex_buffer += stride;
    }
    auto index_buffer = mesh_data->getIndices32();
    for (size_t i = 0; i < indices.size(); ++i) {
        index_buffer[i] = indices[i];
    }
    auto data = bs::RendererMeshData::convert(mesh_data);
    mesh->writeData(data, false);

    // Read custom shader
    std::string shader_file(SHADER_DIR);
    shader_file.append("simple.bsl");
    auto shader = bs::gImporter().import<bs::Shader>(shader_file.c_str());
    auto material = bs::Material::create(shader);

    // Construct mesh scene object
    auto mesh_so = bs::SceneObject::create("Mesh");
    auto renderable = mesh_so->addComponent<bs::CRenderable>();
    renderable->setMesh(mesh);
    renderable->setMaterial(material);
    auto bsphere = mesh->getProperties().getBounds().getSphere();
    auto scale = 1.0f / bsphere.getRadius();
    mesh_so->setScale(bs::Vector3(scale, scale, scale));
    mesh_so->setPosition(-bsphere.getCenter());

    // Construct camera scene object
    auto window = bs::gApplication().getPrimaryWindow();
    auto window_props = window->getProperties();
    auto camera_so = bs::SceneObject::create("Camera");
    auto camera = camera_so->addComponent<bs::CCamera>();
    camera->getViewport()->setTarget(window);
    camera->setProjectionType(bs::PT_PERSPECTIVE);
    auto aspect = static_cast<float>(window_props.width) / window_props.height;
    camera->setAspectRatio(aspect);
    camera->setHorzFOV(bs::Degree(90.0f));
    camera->setNearClipDistance(0.001f);
    camera->setFarClipDistance(10.0f);
    camera_so->setPosition(bs::Vector3(2.0f, 2.0f, 2.0f));
    camera_so->lookAt(bs::Vector3(0, 0, 0));

    // Main loop
    bs::Application::instance().runMainLoop();
    bs::Application::shutDown();

    return 0;
}

I’m using the 1.0b version, under Archlinux using gcc-7.3.1.