Esquema de suporte do mecanismo de renderização para Web

fundo

WebAssembly

Simplificando, WebAssembly pode nos ajudar a compilar C++ em um formato binário .wasm que pode ser chamado por JS e usá-lo no lado da Web para chamar módulos C++ no lado da Web, aproveitando ao máximo as vantagens de linguagem eficiente do C++ .

Instalar Emscripten3.1.0

Emscripten é uma cadeia de ferramentas completa do compilador WebAssembly.
Site oficial: https://emscripten.org/index.html
Documentação oficial de instalação: https://emscripten.org/docs/getting_started/downloads.html
Siga as etapas de instalação na documentação oficial para concluir com êxito a instalação do Emscripten e o documentação oficial Existem instruções mais detalhadas sobre o uso do Emscripten, recomenda-se pesquisar e consultar a documentação oficial, se necessário.
Além disso, o documento de referência é o seguinte:
https://www.cntofu.com/book/150/zh/ch1-quick-guide/readme.md

Interação entre C++ e Javascript

ligar

maneira original

Os métodos em C++ podem ser extern "C" EMSCRIPTEN_KEEPALIVEmodificados e podem ser chamados em html.
extern "C" - Convertido para interface C,
EMSCRIPTEN_KEEPALIVEmacro - indica que este método precisa ser mantido para não ser otimizado o tempo todo.
Adicione "_" na frente do método ao chamar, e o nome da função changeColor pode ser pesquisado no código de cola gerado pelo emcc.

//example.cpp
#include <emscripten.h>
extern "C" void EMSCRIPTEN_KEEPALIVE changeColor(){
    
    return 0;}

//emcc
emcc --bind example.cpp -o index.js -O3 -s WASM=1 
	  <script type='text/javascript'>
        var canv = document.getElementById('canvas');
        var Module = {
    
    canvas: canv};
    </script>    
    <!-- Call the javascript glue code (index.js) as generated by Emscripten -->
    <script src="index.js"></script>//调用index.js    
    <!-- Allow the javascript to call C++ functions -->
    <script type='text/javascript'>
        canv.addEventListener('click',    _changeColor, false);//使用index.js中方法
    </script>

No meio

Embind é usado para vincular funções e classes C++ a JavaScript para que o código compilado possa ser usado em js de maneira natural:

  1. Um arquivo de cabeçalho precisa ser adicionado ao código C/C++ #include <emscripten/bind.h>.
  2. Use EMSCRIPTEN_BINDINGS()blocos para criar associações para funções, classes, tipos de valor, ponteiros (tanto ponteiros brutos quanto inteligentes), enums e constantes
  3. Adicione o parâmetro --bind ao compilar

Para vincular classes, funções, propriedades e estruturas, consulte os seguintes exemplos:

//example.cpp
#include <emscripten/bind.h>
using namespace emscripten;
struct Point {
    
    
    int x;
    int y;
};
 
Point getPoint() {
    
    
    Point point = {
    
    0};
       point.x = 100;
       point.x = 200;
       return point;
}

class MyClass {
    
    
public:
  MyClass(int num){
    
     m_num = num; };
  void CompareBig(int x, int y){
    
                    //普通函数
  	  printf("Big one is %d\n", x > y ? x : y);}
  static int getNum(const MyClass& instance){
    
       //静态函数
      return instance.printfNum;}
  int getNumValue() const{
    
                          //可与下函数绑定属性
		printf("getNumvalue:%d\n", m_num);return m_num;}
  void setNum(int num){
    
                             //可与上函数绑定属性
		printf("setNum:%d\n", num);m_num = num;}
private:
  int printfNum(){
    
    
	  return m_num;}
private:
  int m_num;
};
 
EMSCRIPTEN_BINDINGS(my_module) {
    
     //my_module可以随意填写
    //绑定结构体
    value_object<Point>("Point")
        .field("x", & Point::x)
        .field("y", & Point::y);
    //绑定函数
    function("_getPoint", &getPoint);
    //绑定类、类中函数、属性
    class_<MyClass>("MyClass")
    .constructor<int>() //构造函数
    .function("CompareBig", &MyClass::CompareBig) //普通类成员函数
    .class_function("getNum", &MyClass::getNum) //静态类成员函数
    .property("m_num", &MyClass::getNumValue, &MyClass::setNum)//绑定属性,将私有变量暴露
    ;
}

emcc --bind -o index.js example.cpp -O3 -s WASM=1 
//html中的js代码
//调用函数
var oPoint = Module._getPoint();
var ix = oPoint.x;
var iy = oPoint.y;
//调用类
var instance = new Module.MyClass(10); //声明类的对象
instance.CompareBig(1,2); //类函数
Module.MyClass.getNum(instance); //静态函数
instance.m_num = 20; //绑定属性后可直接对变量赋值
instance.delete(); //使用后需释放,否则Emscripten堆将无限增长

ponteiro inteligente

1. Ligação de ponteiros inteligentes comuns

//绑定智能指针的两种方式
EMSCRIPTEN_BINDINGS(module) {
    
    
  class_<Class>("Class")
      .constructor<int>()
      .smart_ptr<std::shared_ptr<Class>>("shared_ptr<Class>")//智能指针
      .property("x", &Class::getX, &Class::setX);
}

EMSCRIPTEN_BINDINGS(module) {
    
    
  class_<Class>("Class")
       //这里将智能指针与类对象的创建过程进行绑定 
      .smart_ptr_constructor("shared_ptr<Class>", &std::make_shared<Class, int>)
      .property("x", &Class::getX, &Class::setX);
};

2. Vinculação de ponteiros inteligentes personalizados
2.1 Escreva uma smart_ptr_traitclasse de modelo personalizada para implementar seu próprio tipo de ponteiro inteligente.

//编写自定义smart_ptr_trait模板类,实现自己的智能指针类型
template<typename PointeeType>
struct smart_ptr_trait<dan::Smart_Object<PointeeType>> {
    
    
    typedef dan::Smart_Object<PointeeType> PointerType;
    typedef typename PointerType::element_type element_type;

    static element_type* get(const PointerType& ptr) {
    
    
        return ptr.get();
    }

    static sharing_policy get_sharing_policy() {
    
    
        return sharing_policy::BY_EMVAL;
    }

    static dan::Smart_Object<PointeeType>* share(PointeeType* p, EM_VAL v) {
    
    
        return new dan::Smart_Object<PointeeType>(
            p,
            val_deleter(val::take_ownership(v)));
    }

    static PointerType* construct_null() {
    
    
        return new PointerType;
    }

private:
    class val_deleter {
    
    
    public:
        val_deleter() = delete;
        explicit val_deleter(val v)
            : v(v)
        {
    
    }
        void operator()(void const*) {
    
    
            v();
            // eventually we'll need to support emptied out val
            v = val::undefined();
        }
    private:
        val v;
    };
};

2.2 Adicionar na classe smart pointer element_typePara construir smart_ptr_trait, esta variável deve existir no smart pointer.

    using element_type = typename remove_extent<T>::type;

![image.png](https://img-blog.csdnimg.cn/img_convert/f5bfd22713555967e83677aded7a96da.png#crop=0&crop=0&crop=1&crop=1&height=181&id=Li64e&margin=[object Object]&name=image.png&originHeight=181&originWidWid =707&originalType=binary&ratio=1&rotation=0&showTitle=false&size=12542&status=done&style=none&title=&width=707) 2.3.在中
绑定EMSCRIPTEN_BINDINGS

EMSCRIPTEN_BINDINGS(my_module) {
    
      
     class_<SGGeoPoint>("SGGeoPoint")   
             .constructor()
             .smart_ptr<SGGeoPointPtr>("SGGeoPointPtr")//自定义智能指针
             .function("point",&SGGeoPoint::point)
             .function("getX",&SGGeoPoint::getX)
             .function("getY",&SGGeoPoint::getY);
};

classe de interface

O exemplo a seguir mostra o método de ligação da classe de interface.

//定义一个接口类,该接口需要由子类来实现
class MyInterface{
    
    
    public:      
        virtual void invoke(const std::string &str) = 0;  //纯虚函数
}; 
//定义一个胶水类用来链接C/C++与js代码
class DerivedClass : public wrapper<MyInterface> {
    
    
    public:
        EMSCRIPTEN_WRAPPER(DerivedClass);
        void invoke(const std::string &str) override{
    
           	
            return call<void>("invoke",str);} //间接调用在js中实现的方法       
};
//绑定 
EMSCRIPTEN_BINDINGS(module){
    
    
    class_<MyInterface>("MyInterface")
    //纯虚函数:绑定父类中的抽象接口
    .function("invoke",&MyInterface::invoke,pure_virtual())
    //通过allow_subclass方法向绑定的接口添加俩个js方法extend和inplement,用于实现定义在c++代码中的接口
    .allow_subclass<DerivedClass>("DerivedClass");
}

No código acima, uma **classe "glue"** usada para conectar o código e o ambiente **wrapper**é construída por meio da classe template . Dentro dessa classe, o processo de implementação da classe de interface no código e da subclasse no ambiente é vinculado indiretamente chamando a interface de subclasse implementada no código . Ao vincular internamente o método abstrato definido na classe de interface, é necessário fornecer ao método um sinalizador de estratégia nomeado, que identificará o processo de vinculação da função virtual pura e fornecerá a capacidade de captura de exceção correspondente. Fornece dois métodos de função local que podem ser usados ​​para implementar a interface C/C++ no código, ou seja, o método sum . Mas a premissa de usar esses dois métodos é que, ao vincular a classe de interface, é necessário declarar explicitamente por meio do método que o processo de implementação específico da classe de interface será concluído no ambiente. Em seguida, você pode usar esses dois métodos para implementar a lógica específica da interface no ambiente.C/C++JavaScriptJavaScriptC++JavaScriptEMSCRIPTEN_BINDINGSfunction**pure_virtual()**
EmbindJavaScript**extend****implement****allow_subclass**JavaScriptJavaScriptC/C++

//通过extend方法来实现子类         
            var DerivedClass = Module.MyInterface.extend("MyInterface",
            {
    
    
                //构造方法(可选)
                __construct: function(){
    
    
                    this.__parent.__construct.call(this); //调用父类的构造函数
                },
                //析构函数(可选)
                __destruct: function(){
    
    
                    this.__parent.__destruct.call(this); //调用父类的析构函数
                },
                //对接口中纯虚函数的具体实现
                invoke: function(str){
    
    
                    console.log("js_invoke_ing" + str);
                },
            });
						//调用子类方法
            var instanceExtend = new DerivedClass;
            instanceExtend.invoke("i'm extend");
//通过implement方法来构造子类
            var x = {
    
    
                invoke:function(str){
    
    
                    console.log("invoking with:"+ str);
                }
            };
            var interfacePbject = Module.MyInterface.implement(x);
						//调用子类方法
            interfacePbject.invoke("i'm implement");

extend : Neste código, o processo de implementação da subclasse da classe de interface é primeiro **extend**concluído usando o método . InterfaceSemelhante ao C/C++processo de implementação da classe de manutenção, aqui você também pode usar seletivamente o método __constructou __destructpara adicionar construtores e destruidores correspondentes à classe de entidade.
**implemento:** em comparação com extendos métodos, **implement**os métodos são mais adequados para classes de interface simples que não requerem construtores e destruidores. JavaScriptPode-se ver que aqui só é necessário agrupar a função com a mesma assinatura que a função virtual pura na classe de interface com a estrutura do objeto e passá-la para implemento método derivado do objeto de classe vinculado para concluir o processo de implementação da interface aula. Mais convenientemente, esse método retornará diretamente um objeto de subclasse instanciado, o que também evita a necessidade de outro newprocesso.

Substituindo uma função virtual não pura

//定义一个接口类,该接口需要由子类来实现
class MyInterface{
    
    
    public:
    	//非纯虚函数
    	virtual void invokeN(const std::string &str){
    
    
            std::cout << str + " - from 'c++'"<<std::endl;}
}; 
//定义一个胶水类用来链接C/C++与js代码
class DerivedClass : public wrapper<MyInterface> {
    
    
    public:
        EMSCRIPTEN_WRAPPER(DerivedClass); 
   		void invokeN(const std::string &str) override{
    
           	
            return call<void>("invokeN",str);} //间接调用在js中实现的方法
};
//绑定 
EMSCRIPTEN_BINDINGS(module){
    
    
    class_<MyInterface>("MyInterface")
    //非纯虚函数:需要通过optional_override方法来创建特殊的Lambda函数,防止js代码与Wrapper函数之间产生循环递归调用问题 
    .function("invoke",optional_override([](MyInterface &self,const std::string &str){
    
    
        return self.MyInterface::invoke(str);
    }))
    //通过allow_subclass方法向绑定的接口添加俩个js方法extend和inplement,用于实现定义在c++代码中的接口
    .allow_subclass<DerivedClass>("DerivedClass");
}

No geral, a única diferença entre este código e o código anterior é que, ao vincular a função virtual não pura da classe abstrata, o ponteiro da função correspondente não pode ser passado diretamente para o método da função, mas o processo de chamada da classe abstrata A função precisa ser encapsulada pelo optional _verridemétodo em uma função anônima especial e passada para o método da função como um todo. Além disso, ao contrário do processo de implementação de uma classe de interface, podemos substituir seletivamente ou usar diretamente a implementação padrão da função de chamada no ambiente JavaScript.O processo específico de substituição só pode **extend**ser realizado por meio de herança .

//通过extend方法来实现子类         
            var DerivedClass = Module.MyInterface.extend("MyInterface",
            {
    
    
                //选择性地对接口中非纯虚函数的具体实现
                invokeN: function(str){
    
    
                    console.log("js_invokeN_ing" + str);
                }
            });
						//调用子类方法
            var instanceExtend = new DerivedClass;
            instanceExtend.invokeN("i'm extend");

Classes derivadas em C++

//定义一个基类(父类) 
class MyBaseClass{
    
    
    public:
        MyBaseClass() = default;
        virtual std::string invoke(const std::string &str){
    
    
            return str + " - from 'MyBaseClass'"; };
};
//定义继承的子类
class MyDerivedClass : public MyBaseClass{
    
    
    public:
        MyDerivedClass() = default; 
        std::string invoke(const std::string &str) override{
    
    
            return str + " - from 'MyDerivedClass'"; };
};
//绑定 
EMSCRIPTEN_BINDINGS(module){
    
    
    //绑定基类 
    class_<MyBaseClass>("MyBaseClass")
    .constructor<>()
    .function("invoke",&MyBaseClass::invoke);
    //绑定子类
    class_<MyDerivedClass,base<MyBaseClass>>("MyDerivedClass")
    .constructor<>()
    .function("invoke",&MyDerivedClass::invoke);
}

função sobrecarregada

Use select_overload()a função auxiliar para selecionar a assinatura apropriada.

//示例说明
struct Example {
    
    
    void foo();
    void foo(int i);
    void foo(float f) const;
}; 
EMSCRIPTEN_BINDING(overloads) {
    
    
   class_<Example>("Example")
     .function("foo", select_overload<void()>(&Example::foo))
     .function("foo_int", select_overload<void(int)>(&Example::foo))
     .function("foo_float", select_overload<void(float)const>(&Example::foo))
     ;
}

O seguinte demonstra o construtor de SGGeoPoint e a função sobrecarregada de createObject.

class_<SGGeoPoint>("SGGeoPoint")
     .constructor()
     .constructor<double,double>()//此行对应参数3有默认值的C++构造函数
     .constructor<double,double,double>()
     .smart_ptr<SGGeoPointPtr>("SGGeoPointPtr")
     .class_function("createObject",select_overload<SGGeoPointPtr()>(&SGGeoPoint::createObject))
     .class_function("createObjectByXYZ",select_overload<SGGeoPointPtr(double,double,double)>(&SGGeoPoint::createObject))
     ;

Valores padrão do parâmetro de função

Para construtores , a associação de parâmetros padrão pode ser implementada com relativa facilidade e funções comuns podem optional_overridecriar funções especiais do Lambda por meio de métodos.
Para a seguinte função com parâmetros

//SGGeoPoint类构造函数、静态函数的参数有默认值
class SMART_GEOMETRY_EXPORT SGGeoPoint :public SGAbstractGeometry
{
    
    
public:
	SGGeoPoint();
	SGGeoPoint(double x, double y, double z = 0.0);//带默认值
	static dan::Smart_Object<SGGeoPoint> createObject();
	static dan::Smart_Object<SGGeoPoint> createObject(double x, double y, double z = 0.0);//带默认值
}

Escrita obrigatória correspondente:

class_<SGGeoPoint>("SGGeoPoint")
        .constructor()
        .constructor<double,double>()//构造函数直接这样写即可
        .constructor<double,double,double>()
        .smart_ptr<SGGeoPointPtr>("SGGeoPointPtr")
        .class_function("createObject",select_overload<SGGeoPointPtr()>(&SGGeoPoint::createObject))
        .class_function("createObject_1",select_overload<SGGeoPointPtr(double,double,double)>(&SGGeoPoint::createObject))
        //普通函数通过lamda函数可达到需求
        .class_function("createObject_2",optional_override([](double x,double y){
    
    
                return SGGeoPoint::createObject(x,y);
            }))
        ;

enumerar

embind suporta enumerações C++98 e classes de enumeração C++11.

    enum OldStyle {
    
    
        OLD_STYLE_ONE,
        OLD_STYLE_TWO
    }; 
    enum class NewStyle {
    
    
        ONE,
        TWO
    };
    EMSCRIPTEN_BINDINGS(my_enum_example) {
    
    
        enum_<OldStyle>("OldStyle")
            .value("ONE", OLD_STYLE_ONE)
            .value("TWO", OLD_STYLE_TWO);
        enum_<NewStyle>("NewStyle")
            .value("ONE", NewStyle::ONE)
            .value("TWO", NewStyle::TWO);
     }

O formulário de chamada em JavaScript é o seguinte:

    Module.OldStyle.ONE;
    Module.NewStyle.TWO;

constante

    EMSCRIPTEN_BINDINGS(my_constant_example) {
    
    
        constant("SOME_CONSTANT", SOME_CONSTANT);
    }

Link de referência do Embind :
https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html
https://www.osheep.cn/3952.html
https://www.jianshu.com/p/a03444bf9e97
https:/ /www.cnblogs.com/catwin/p/13337074.html

tela

Emscripten fornece suporte para EGL e OpenGL. Para obter detalhes, consulte a documentação oficial: https://emscripten.org/docs/porting/multimedia_and_graphics/index.html

Tela escrita

Existe apenas uma tela única no Emscripten e o tamanho pode ser especificado pelo id da tela.

EGL cria contexto

1. Obtenha o identificador do objeto
EGLDisplay eglGetDisplay(EGL_DEFAULT_DISPLAY)
2. Inicialize na exibição
EGLBoolean eglInitialize()
3. Encontre os parâmetros de destino de renderização
eglGetConfigs()/eglChooseConfig()
4. Crie a superfície de destino de renderização principal
EGLSurface eglCreateWindowSurface()
5. Crie um contexto de renderização GLES2, a versão pode ser especificada como ES2/ES3 ao criar
EGLContext eglCreateContext()
6. Ative o contexto de renderização
eglMakeCurrent()

OpenGLES3

Se o OpenGLES3 for usado, os parâmetros precisam ser especificados -s FULL_ES3=1.
Observe que a primeira linha do shader usado pelo opengles3 no programa deve especificar a versão **#version 300 es**.

Tela de encadernação JS

Vincule a tela padrão em C++ ao elemento canvas canvas de JS da seguinte maneira.

<body>    
    <!-- Create the canvas that the C++ code will draw into -->
    <canvas id="canvas" oncontextmenu="event.preventDefault()"></canvas>
    <!-- Allow the C++ to access the canvas element --> 
    <script type='text/javascript'>
        var canv = document.getElementById('canvas');
        var Module = {
    
    
        canvas: canv      
        };
    </script>
    <script type='text/javascript' src="smartgis.3dexample.js"></script>
</body>

sistema de arquivos virtual

Sistema de arquivos Emscripten

Emscripten fornece o sistema de arquivos de memória MEMFSfopen() para // e outras chamadas de função fread()de acesso a arquivos libc/libcxx. Link de referência: https://www.cntofu.com/book/150/zh/ch3-runtime/ch3-03-fs.md https://emscripten.org/docs/porting/files/packaging_files.htmlfwrite()


pré-carga emcc

O empacotamento do arquivo pode ser feito na linha de comando do emcc, existem duas formas: preload (preload) e embed (embed). Entre eles , o método de pré-carregamento é mais eficiente e o tamanho do arquivo é menor após o empacotamento. Após o empacotamento, é gerado um arquivo de dados .data com o mesmo nome de .js, que contém os dados binários de todos os arquivos. Operações de download e carregamento .
Exemplo de uso em emcc:

--preload-file hello.txt //指定打包文件
--preload-file filedir //指定打包文件夹下所有文件,包含下级文件夹中的文件

arquivo de pacote CMAKE

需要打包的文件:
![image.png](https://img-blog.csdnimg.cn/img_convert/7f3a9b3c37f91b01e25f66487c29aa73.png#clientId=u275b0154-1d4f-4&crop=0&crop=1&cropid=0&paste=0&cropight=0&cropight=0&cropight=0&cropight=0&cropight=0&cropight=0&cropight=0&cropight=0&cropight=0&cropid= F2zgW&margin=[object Object]&name=image.png&originHeight=118&originWidth=1180&originalType=binary&ratio=1&rotation=0&showTitle=false&size=36560&status=done&style=none&taskId=ued147289-c81b-4c43-a57f-43612e&5wica5dbth=9)

  1. --preload-fileEspecifique o arquivo/pasta do pacote arquivado em CMakeLists.txt
set_target_properties(test PROPERTIES LINK_FLAGS "--bind --preload-file shaders")
  1. Os arquivos/pastas empacotados precisam ser colocados no diretório cmake do projeto

  2. Após a compilação, o programa poderá chamar os shaders no arquivo empacotado.

Memória

definir memória

imagem.png
Se a memória for insuficiente quando o Em for compilado, podemos modificar o valor em settings.js no diretório emsdk/upstream/emscripten/src INITIAL_MEMORYpara definir uma memória maior.
Erros de memória insuficiente também podem ocorrer durante o tempo de execução no lado da Web, mas o tamanho dos dados flutua enormemente durante o tempo de execução e seria um desperdício especificar uma memória grande durante a compilação inicial. O parâmetro de memória variável pode ser especificado para expandir a capacidade de memória em tempo de execução.

memória variável

Usando -s ALLOW_MEMORY_GROWTHo modo, a memória variável pode afetar o desempenho ao compilar para asm.js. Mas quando o destino da compilação é wasm, usar o modo de memória mutável é muito eficiente e não afetará o desempenho.

depuração

compilar projeto

Projeto CMake

o código

  1. Defina o modo de renderização para OpenGLES3

SGERenderer::setCurrentRenderer(“OpenGLES3”);

  1. emscripten_set_main_loop define o loop de mensagem em
#include <emscripten.h>
std::function<void()> loop;
void main_loop() {
    
     loop(); }
int main()
{
    
    
    //.....
	loop = [&]
    {
    
    
		//draw......
    };
    emscripten_set_main_loop(main_loop, 0, true);
	return 0;
}
  1. versão específica do shader

No topo (primeira linha) do arquivo shader, a versão deve ser declarada:

# version 300 es 

dependências da biblioteca

Durante a compilação do projeto cmake, as bibliotecas dependentes precisam ser adicionadas ao arquivo CMakelists.txt. Se a biblioteca A depender da biblioteca B e a classe ou método da biblioteca B for usada no código, a biblioteca B precisa ser adicionada como uma dependência:

link_directories(/mnt/hgfs/smartgis.all/smartgis.all/bin/x64/Release)
link_directories(/home/czw/3rd_a)
target_link_libraries(testtwo stdc++ dl smartgis.core.a smartgis.common.a
smartgis.data.a smartgis.geometry.a geos.a m)
set_target_properties(testtwo PROPERTIES LINK_FLAGS "--bind --experimental-wasm-simd")

Após a compilação, restam apenas arquivos .wasm e .js, que encapsulam o código no projeto e não incluem pacotes de bibliotecas de terceiros, portanto, o tamanho do arquivo não será muito grande.

Parâmetro Em

A seguir estão alguns parâmetros de compilação emscripten, que podem ser especificados sob demanda.

--bind 	                    #执行C++到JS地绑定
--experimental-wasm-simd    #SIMD
-std=c++11  
-s WASM=1                   #生成.wasm而不是asm.js
-s FULL_ES3=1               #使用gles3.0
--preload-file shaders      #打包文件
-s LLD_REPORT_UNDEFINED     #指定对undefined类型错误更详细地输出
-s ALLOW_MEMORY_GROWTH      #允许程序运行时内存增长
-s ASSERTIONS=1             #断言,指定后Web运行时可输出更多错误信息
-v                          #编译时输出更多信息
-O3                         #优化编译

CMAKE

Execute a compilação de biblioteca estática para cada biblioteca da qual o programa depende.
janelas:

emsdk_env.bat   //注册em环境
emcmake cmake ..   //cmake
emmake make     ///make

linux:

#1.注册环境:进入emsdk目录后执行emsdk_env.sh
source ./emsdk_env.sh
#2.cmake
emcmake cmake ..
#3.make
emmake make

Finalmente, três arquivos: smartgis.3dexample.js, smartgis.3dexample.wasm e smartgis.3dexample.data são compilados.

iniciar web

Crie um novo arquivo index.html no diretório em execução, importe smartgis.3dexample.js, especifique a conexão da tela e execute as seguintes operações:

emrun --no_browser --port 8080 .

Abra a saída da URL pela janela de comando no navegador para visualizar o efeito no navegador. Se não houver conexão, altere 0.0.0.0 no endereço para localhost para atualizar.
Além do emrun, você também pode usar os seguintes métodos para iniciar o serviço.

python -m http.server 8080
//如果是python2,则使用下面代码
//python -m SimpleHTTPServer 8080

Efeito de renderização OpenGL

No momento, foi verificado preliminarmente que o mecanismo autônomo permite que o modo de renderização OpenGLES3 seja renderizado no lado da Web.

desenhar triângulo

O seguinte é o efeito exibido no lado da Web depois de desenhar o triângulo. Shaders são carregados em packfiles.
imagem.png

Carregar IFC

A seguir está a exibição do modelo ifc renderizado pelo mecanismo autônomo na web. Afetado pela impressão contínua de informações, o número de quadros na captura de tela é pequeno. Mas o número de quadros é de fato um problema que o motor precisa otimizar no futuro.
gif-ifc楼.gif

Acho que você gosta

Origin blog.csdn.net/qq_33377547/article/details/126596652
Recomendado
Clasificación