美文网首页
2022-06-14 Canvas画布模糊问题

2022-06-14 Canvas画布模糊问题

作者: SMSM | 来源:发表于2022-06-14 15:38 被阅读0次

    【重要】view大小(属性值决定最终上屏时的缩放比例) 画布bufffer大小 gl.viewPort视口大小(映射画布坐标到NDC坐标 -1到1 GL世界的坐标)

    所有view单位 和 buffer单位不一致的,都要 渲染引擎提供处理的。

    threejs的处理流程

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(screenCanvas.clientWidth, screenCanvas.clientHeight);

    skottie的处理流程

    json文件内为view单位,创建buffer用的物理像素,绘制通过render 给定dstR 做映射,实现了view单位到物理像素单位的映射。

        auto canvas = skottieAnimation_->mRenderTarget->getCanvas();
        // canvas->clear(backgroundColor);
    
        SkAutoCanvasRestore acr(canvas, true);
        SkRect bounds = SkRect::MakeWH(width_, height_);
        skottieAnimation_->mAnimation->render(canvas, &bounds);
        canvas->flush();
    

    void Animation::render(SkCanvas* canvas, const SkRect* dstR) {
    const SkRect srcR = SkRect::MakeSize(this->size());
    if (dstR) {
    canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
    }
    }

    WebCanvas的流程

    要自己手动调整和映射。canvas.scale

    如何解决画布模糊问题 https://opendocs.alipay.com/support/01rb86
    画布大小、图片绘制尺寸、保存图片实际尺寸。viewsize、buffersize。

    1. 逻辑像素 * pixelRatio = 设备像素
    2. 原因是 清晰度问题。attribute width/height决定了画布大小(逻辑像素)。css_style决定view大小(设备像素)。drawapi中的单位是画布单位。小画布 上屏到大容器view中,被放大导致的模糊。
      大画布绘制到小view中,也会模糊。
    3. canvas中的变换矩阵。transform。移动缩放倾斜。不包含旋转。https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/transform
    4. 矩阵的乘法。都是先行。M*N M是多少行。乘的时候从第一个矩阵的第一行开始,乘以第二个矩阵的第一列。


      一个m×n的矩阵就是m×n个数排成m行n列的一个数阵
    图像的几何变换主要分为三类:刚性变换、仿射变换和透视变换

    https://zhuanlan.zhihu.com/p/80852438

    1. 仿射变换&透视变换 正交投影&透视投影(近大远小) fov(视野可视角度)
      https://hyiker.com/2021/03/25/%E6%AD%A3%E4%BA%A4%E6%8A%95%E5%BD%B1-%E9%80%8F%E8%A7%86%E6%8A%95%E5%BD%B1/
      5.1 canvas中提供的变换矩阵,3*3的不支持旋转角,支持了个扭曲拉伸 仿射变换。
    2. 变换矩阵 。https://pengfeixc.com/blog/60a7492be97367196dce3eef

    https://www.w3schools.com/tags/tryit.asp?filename=tryhtml5_canvas_height_width_clear

    <!DOCTYPE html>
    <html>
    <body>

    <img src="https://www.w3schools.com/graphics/pic_the_scream.jpg" id="tupian">
    <canvas id="myCanvas" width="200" height="200" style="width:200px;height:200px;border:1px solid">
    Your browser does not support the HTML5 canvas tag.
    </canvas>

    <script>
    var c = document.getElementById("myCanvas");
    var pixelRatio = 2;
    c.width = 200 * pixelRatio;
    c.height = 200 * pixelRatio;
    var ctx = c.getContext("2d");
    //ctx.scale(pixelRatio, pixelRatio)

    var img = document.getElementById("tupian");
    ctx.drawImage(img, 10, 10);

    ctx.fillStyle = "#92B901";
    ctx.fillRect(50, 50, 100, 100);
    ctx.font = "30px Arial";
    ctx.fillText("Hello World"+pixelRatio, 10, 50);

    </script>

    <button onclick="clearCanvas()">Clear canvas</button>

    </body>
    </html>

    前端css尺寸px是逻辑像素呀,并且window.innerWidth也会逻辑像素。在做threejs的适配是,没有单独设置 canvas.width(决定物理像素) ,而是修改的 canvas.style={ width: width + "px",} (决定虚拟像素) 。 如果不设置 width 的话,默认 canvas.width = canvas.style.width * pixleRadio 。 适配threejs没有设置width,所以要传递给devicePixelRatio 给threejs 让他内部自己换算到物理物理像素。screenCanvas.clientWidth == viewWidth; viewWidth = getIntValue(viewWidth, w); bufferWidth = getIntValue(bufferWidth, w);

    这样的话,绘制接口的坐标 可以走CSS(虽然绘制API单位是物理像素,但是threejs内部有canvas.scale的设置,保证了可以走CSS单位)

    如果需要自己调整画布大小呢,用于降级,则可以,自己给定一个devicePixelRatio值,并手动设置canvas.width 和 renderer.setPixelRatio 。

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(screenCanvas.clientWidth, screenCanvas.clientHeight);

    skottie的流程是

    尺寸问题

    1. webcanvas中如何绘制高分辨率,是通过 手动 把逻辑像素乘以屏幕密度,然后再canvas.scale放大2倍,相当于让原绘制逻辑中的坐标、长度单位也同步放大2倍,保证了不需要修改代码。

    2. NativeCanvas的解决方案是 直接修改surface的物理像素大小。但是所有的绘制接口用于生成三角形顶点的单位依然是逻辑像素,这样生成归一化的坐标还是正确的。glviewport要使用物理像素大小。

    view大小(属性值决定最终上屏时的缩放比例) 画布bufffer大小 gl.viewPort视口大小(映射画布坐标到NDC坐标 -1到1 GL世界的坐标)

    ViewPort指定坐标比画布buffer大时,绘制超出画布buffer,走Per-Sample_Processing 相关的流程 做裁剪。 Early Fragment Test 可能是有 被剔除

    1. surfaceholder修改buffer的物理像素的方法
      surface_1.getHolder().setFixedSize(100, 100);
      surfaceChanged{ surfaceWidth = holder.getSurfaceFrame().width();}

    2. glViewport设置 可以比画布buffer大 ,但是只绘制 重合部分 。
      【重要】绘制用物理内存

    前端html中定义tag的宽高,映射到native决定了自身view的大小和宽高radio。
    surface画布物理大小在内存降级时,可以自己降低scale屏幕密度值来减少内存占用,但是要保持radio。
    如果radio不一致,则会存在拉伸,在上屏时,由系统根据View的宽高属性,拉伸画布。

    在一块很大的画布上,居中绘制,则可以通过glviewport来实现的。
    但是如何保持让2d绘制依然使用 逻辑像素单位呢,主要为了生成顶点作用用的。

    1. skottie中逻辑像素单位,在每次绘制时都指定,在最终绘制时,内部走了
      auto canvas = skottieAnimation_->mRenderTarget->getCanvas();
      // canvas->clear(backgroundColor);

       SkAutoCanvasRestore acr(canvas, true);
       SkRect bounds = SkRect::MakeWH(width_, height_);
       skottieAnimation_->mAnimation->render(canvas, &bounds);
       canvas->flush();
      

    void Animation::render(SkCanvas* canvas, const SkRect* dstR) {
    const SkRect srcR = SkRect::MakeSize(this->size());
    if (dstR) {
    canvas->concat(SkMatrix::MakeRectToRect(srcR, *dstR, SkMatrix::kCenter_ScaleToFit));
    }
    }

    相关文章

      网友评论

          本文标题:2022-06-14 Canvas画布模糊问题

          本文链接:https://www.haomeiwen.com/subject/oqyzmrtx.html