Skip to content

cesium vertex shader cant convert float indexes to uint #12992

@xiaozhiquan

Description

@xiaozhiquan

What happened?

it may be a bug on cesium instance attribute.I passed a spatialIndex vertex attribute as an instance attribute to the vertex shader. This attribute was used as a texture index to obtain the vertex coordinates. I found that replacing the index with gl_instanceID could render normally on mobile devices.it seems to turn out that the uint function cannot perform in cesium on mobile phone.even if i use many differerent cesium version
below is my code:

Reproduction steps

var SPECTOR = require("spectorjs");

var spector = new SPECTOR.Spector();

spector.displayUI();
var CesiumViewer;
var gsPrimitiveCollection = [];


// 6*4 + 4 + 4 = 8*4
// XYZ - Position (Float32)
// XYZ - Scale (Float32)
// RGBA - colors (uint8)
// IJKL - quaternion/rot (uint8)
const rowLength = 3 * 4 + 3 * 4 + 4 + 4;

function generateTexture (buffer, vertexCount) {
  if (!buffer) return;

  const f_buffer = new Float32Array(buffer);

  let texwidth = 1024 * 1;
  let texheight = Math.ceil((1 * vertexCount) / texwidth);

  // 4 components per pixel (RGBA)
  let texdata = new Uint32Array(texwidth * texheight * 4);
  //let texdata_c = new Uint8Array(texdata.buffer);
  let texdata_f = new Float32Array(texdata.buffer);



  for (let i = 0; i < vertexCount; i++) {
    // x, y, z
    texdata_f[12 * i + 0] = f_buffer[8 * i + 0];
    texdata_f[12 * i + 1] = f_buffer[8 * i + 1];
    texdata_f[12 * i + 2] = f_buffer[8 * i + 2];


  }

  return {
    // texdata:texdata, 
    texdata: texdata_f,
    texwidth: texwidth,
    texheight: texheight
  }
}

// abstract primitive to hook into render pipe
class CustomPrimitive {
  constructor(options) {

    this.uniformMap = options.uniformMap;
    this.vertexShaderSource = options.vertexShaderSource;
    this.fragmentShaderSource = options.fragmentShaderSource;
    this.renderState = options.renderState;
    this.modelMatrix = options.modelMatrix;
    this.instanceCount = options.instanceCount;
    // 传过来后在叠加pick color vbo
    this.vertexAttributes = options.vertexAttributes
    this.vertexArray = options.vertexArray;
    this.context = options.context
    //this.framebuffer = options.framebuffer;
    this.show = true;
    this.commandToExecute = undefined;
    this.depthPass = undefined;
    this.texdata = options.texdata;

    this.center = options.center || [0, 0];

    this.colorVertextBuffer = undefined
    this.pickIdColors = []
    this.minmax = options.minmax

    var centerCartension = Cesium.Cartesian3.fromDegrees(
      this.center[0],
      this.center[1],
      0,
      CesiumViewer.scene.globe.ellipsoid
    );
    this.boundingSphere = new Cesium.BoundingSphere(centerCartension, 10000);
    var _this = this



    this.vertexArray = this.createVertextArray(this.vertexAttributes, this.context)



  }
  createVertextArray (attributes, context) {
    const splatsVertexArray = new Cesium.VertexArray({
      context: context,
      attributes: attributes,
    });

    return splatsVertexArray
  }
  createCommand (context) {
    var shaderProgram = Cesium.ShaderProgram.fromCache({
      context: context,
      attributeLocations: this.vertexArray.attributes,
      vertexShaderSource: this.vertexShaderSource,
      fragmentShaderSource: this.fragmentShaderSource,
      debugShaders: true,
      logShaderCompilation: true
    });

    var cachedRenderState = Cesium.RenderState.fromCache(this.renderState);

    return new Cesium.DrawCommand({
      owner: this,
      vertexArray: this.vertexArray,
      primitiveType: Cesium.PrimitiveType.POINTS, //Cesium.PrimitiveType.TRIANGLE,////origial
      uniformMap: this.uniformMap,
      modelMatrix: this.modelMatrix,
      instanceCount: this.instanceCount,
      shaderProgram: shaderProgram,
      //framebuffer: this.framebuffer,
      renderState: cachedRenderState,
      // pass: Cesium.Pass.TRANSLUCENT,
      // pickId:"czm_pickColor",
      pass: Cesium.Pass.OPAQUE,
      boundingVolume: this.boundingSphere
    });
  }

  update (frameState) {
    if (!this.show) {
      return;
    }
    if (!Cesium.defined(this.commandToExecute)) {
      this.commandToExecute = this.createCommand(frameState.context);
    }

    frameState.commandList.push(this.commandToExecute);
    // frameState.commandList.push(this.depthPass);
  }

  isDestroyed () {
    return false;
  }

  destroy () {
    if (Cesium.defined(this.commandToExecute)) {
      this.commandToExecute.shaderProgram =
        this.commandToExecute.shaderProgram &&
        this.commandToExecute.shaderProgram.destroy();
    }
    return Cesium.destroyObject(this);
  }
}
function computeModelMatrix (splatCenter) {
  const center = Cesium.Cartesian3.fromDegrees(
    splatCenter[0],
    splatCenter[1],
    50,
    CesiumViewer.scene.globe.ellipsoid
  );
  let modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);

  const rotationMatrix = Cesium.Matrix4.fromRotationTranslation(
    Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(-0))
  );

  modelMatrix = Cesium.Matrix4.multiply(
    modelMatrix,
    rotationMatrix,
    new Cesium.Matrix4()
  );


  return modelMatrix;
}

function SetupCustomPrimitive (data) {

  let InWebGLContext = CesiumViewer.scene.context;

  let InPointCount = data.vertexCount;
  let texdata = data.texdata;
  let splatTexWidth = data.texwidth;
  let splatTexHeight = data.texheight;
  let center = data.center
  let worldMatrix = data.worldMatrix || Cesium.Matrix4.IDENTITY
  let minmax = data.bounding


  try {

    // const triangleVertices = new Float32Array([-2, -2, 2, -2, 2, 2, -2, 2]);
    // const triangleVertices = new Float32Array([-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0]);
    const triangleVertices = new Float32Array([-1.0, -1.0]);


    var triBuffer = Cesium.Buffer.createVertexBuffer({
      context: InWebGLContext,
      typedArray: triangleVertices,
      usage: Cesium.BufferUsage.STATIC_DRAW,
    });
    var firstIndexDepth = new Float32Array(Math.ceil(InPointCount));
    // var intEncodeFloat = (intNumb) => {
    //   let heih = Math.floor(intNumb / 1024);
    //   let low = intNumb % 1024;

    //   return [heih / 255.0, low / 255.0]
    // }
    for (let i = 0; i < firstIndexDepth.length; i++) {
      // let temp = intEncodeFloat(i)
      // firstIndexDepth[2 * i] = temp[0];
      // firstIndexDepth[2 * i + 1] = temp[1];
      firstIndexDepth[i] = i;
    }
    var orderedSplatIndices = Cesium.Buffer.createVertexBuffer({
      context: InWebGLContext,
      // typedArray: depthIndex,
      typedArray: firstIndexDepth,
      usage: Cesium.BufferUsage.STATIC_DRAW,
    });

    const tempTexture = new Cesium.Texture({
      context: InWebGLContext,
      width: splatTexWidth,
      height: splatTexHeight,
      pixelFormat: Cesium.PixelFormat.RGBA,
      pixelDatatype: Cesium.PixelDatatype.FLOAT,
      source: {
        width: splatTexWidth,
        height: splatTexHeight,
        arrayBufferView: texdata,
      },
      flipY: false,
      sampler: new Cesium.Sampler({
        wrapS: Cesium.TextureWrap.CLAMP_TO_EDGE,
        wrapT: Cesium.TextureWrap.CLAMP_TO_EDGE,
        minificationFilter: Cesium.TextureMinificationFilter.NEAREST,
        magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
      }),
    });

    let uniformMap = {
      u_texture: function () {
        return tempTexture;
      }

    };

    const attributes = [
      {
        index: 0,
        enabled: true,
        vertexBuffer: triBuffer,
        componentsPerAttribute: 2,
        componentDatatype: Cesium.ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 0,
      },
      {
        index: 1,
        enabled: true,
        vertexBuffer: orderedSplatIndices,
        componentsPerAttribute: 1,
        componentDatatype: Cesium.ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 1,
      },
    ];
    let modelMatrix = computeModelMatrix(center);


    //j雅可比矩阵跟threejs不对应----624行
    let vertexShaderSource = `
      #version 300 es
      #ifdef GL_ES
      precision highp float;
      precision mediump int;
      #endif
      uniform sampler2D u_texture; 
      

      in vec2 position;
      in float spatialIndex; 
      
      void main () {
       uint stride = 1u;

      vec4 cen = texelFetch(u_texture, ivec2((stride * (uint(spatialIndex) & 0x3ffu)), uint(spatialIndex) >> 10), 0);
        
      vec4 cam = czm_modelView * vec4(cen.xyz, 1);
      vec4 pos2d = czm_projection * cam;    
      gl_PointSize = 10.0;
        
      gl_Position = pos2d / pos2d.w;



      //  uint rowStride= 1023u;
      //  uint colStride= 10u;
        
      //   float colWith = 1024.0;
      //   float heightWith = 45.0;
      //   float  colnum = mod(spatialIndex,colWith) /(colWith-1.0);
      //   float  rownum = 1.0 - ceil(spatialIndex / colWith)/heightWith;
      //   vec4 cen = texture(u_texture,vec2(colnum, rownum));
      //   vec4 cam = czm_modelView * vec4(cen.xyz, 1);
      //   vec4 pos2d = czm_projection * cam;  
        
        
        
      }
    `.trim();

    let fragmentShaderSource = `
      #version 300 es
      precision highp float;
      precision highp int;
      
      void main () {
      out_FragColor = vec4(1.0,0.0,0.0, 1.0);
      }

    `.trim();

    let splatRenderState = {
      blending: {
        enabled: true,
        equationRgb: Cesium.BlendEquation.ADD,
        //equationAlpha: Cesium.BlendEquation.SUBTRACT,
        equationAlpha: Cesium.BlendEquation.ADD,
        functionSourceRgb: Cesium.BlendFunction.ONE,
        functionSourceAlpha: Cesium.BlendFunction.ONE,
        functionDestinationRgb: Cesium.BlendFunction.ONE_MINUS_SOURCE_ALPHA,
        functionDestinationAlpha: Cesium.BlendFunction.ONE,

      },
      depthTest: {
        enabled: false,
      },
      depthMask: false,
      cull: {
        enabled: true,
        face: Cesium.CullFace.FRONT,
      },
    };

    let vertexSource = new Cesium.ShaderSource({
      sources: [vertexShaderSource],
    });

    let fragmentSource = new Cesium.ShaderSource({
      sources: [fragmentShaderSource],
    });


    var customPrimitive = new CustomPrimitive({
      vertexAttributes: attributes,
      context: InWebGLContext,
      uniformMap: uniformMap,
      center: center,
      modelMatrix: modelMatrix,
      vertexShaderSource: vertexSource,
      fragmentShaderSource: fragmentSource,
      renderState: splatRenderState,
      instanceCount: InPointCount,
      texdata: texdata,
      minmax: minmax
    });

    return CesiumViewer.scene.primitives.add(customPrimitive);
  } catch (err) {
    throw err
  }
}

async function RenderSplat (viewer, center, splatUrl, filenameWithExt) {


  CesiumViewer = viewer
  // splatCenter = center
  // vue.$message.error(splatUrl)
  const url = new URL(splatUrl);
  var req
  try {
    req = await fetch(url, {
      mode: "cors", // no-cors, *cors, same-origin
      credentials: "omit", // include, *same-origin, omit
    })
    if (req.status != 200) {
      throw new Error(req.status + " Unable to load " + req.url)
    }
  } catch (error) {
    // vue.$message.error(error)
    return error
  }
  const rowLength = 3 * 4 + 3 * 4 + 4 + 4;
  const reader = req.body.getReader();
  const fileSize = req.headers.get("content-length")
  var splatData, vertexCount;
  const fileType = filenameWithExt.substr(filenameWithExt.lastIndexOf(".") + 1)
  const splatname = filenameWithExt.substr(0, filenameWithExt.lastIndexOf("."))
  console.log(fileType);

  if (fileType == "splat") {
    splatData = new Uint8Array(fileSize);
    let bytesRead = 0;
    let stopLoading = false;
    //worker发送消息
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;

      splatData.set(value, bytesRead);
      bytesRead += value.length;


    }
    vertexCount = Math.floor(bytesRead / rowLength)


  }



  try {
    let textureInfo = generateTexture(splatData.buffer, vertexCount)
    // 释放文件内存
    splatData = null;

    var primitiveInfo = {
      vertexCount: vertexCount,
      ...textureInfo,
      center: center
    };
    let primitive = SetupCustomPrimitive(primitiveInfo);
    primitive.customId = splatname
    gsPrimitiveCollection.push(primitive)

  } catch (error) {
    return error
  }

  CesiumViewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromDegrees(center[0], center[1], 100),
    duration: 0.5,
    orientation: {
      heading: Cesium.Math.toRadians(30.0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0.0,
    }
  });

  return {
    gsPrimitives: gsPrimitiveCollection
  }
}

export default RenderSplat

Sandcastle example

No response

Environment

Browser:
CesiumJS Version:
Operating System:

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions