以上都是这一个星期碰到的坑,找了很多很多资料,总结归纳一下,希望对你的项目有一点点帮助

先说说需求
1.加载3D模型
2.点击模型的子模型会显示对于子模型名称
3.不全屏展示,还要点击子模型
4.创建控制器的时候注意传入dom
我并没有很全面的学习three,虽然有课程,但是项目进度并不允许,只能踩着石头过河,查找前辈们总结的经验,在这里归纳总结一下

效果图

我这个项目替换自己的obj时可能会报错,因为每个obj文件的子模型的name不一样,解决方法



项目太丑,扎眼了,刚写好,不立刻记下笔记就忘了,自己优化 npm run serve

链接:https://pan.baidu.com/s/1fVZLTfuALxi4Gmpc6hGNfg
提取码:ee0e
复制这段内容后打开百度网盘手机App,操作更方便哦--来自百度网盘超级会员V3的分享

记录这几天挣扎的崩溃的内心

1.vue引入three 不用在main中做配置

import * as THREE from "three";
import { OBJLoader, MTLLoader } from "three-obj-mtl-loader";
import { CSS2DRenderer, CSS2DObject } from "three-css2drender";
const OrbitControls = require("three-orbit-controls")(THREE);

或者直接在package.json中dependencies和devDependencies对象中加入下面的
然后 npm install

 "dependencies": {"three-css2drender": "^1.0.0","three-obj-mtl-loader": "^1.0.3"},"devDependencies": {"three": "^0.123.0","three-obj-mtl-loader": "^1.0.3"},

2.在return中定义若干变量

听说不能直接赋值,vue会监听,也没研究那么深,人家竟然这么说,也就没定义值

data() {return {axes: "",group: "",scene: "",light: "",camera: "",controls: "",renderer: "",directionalLight: "",ambient: "",loader: "", //地板glass_material: "",publicPath: process.env.BASE_URL,m1: "",m2: "",m3: "",mouse: "",objects: [],raycaster: "",getBoundingClientRect: {},offsetWidth: "",offsetHeight: "",theta: 0, //相机旋转角度cord1: {},cord2: {},cord3: {},cord4: {},selectObject: {}, //被点击的第一个元素};},
2.看mounted的钩子有三个方法要执行
mounted() {this.init();this.loadObj();this.animate();},

先看第一个方法 this.init();

就是给return中没赋值的变量赋值成new出来的three实例

这里是:因为我加载的obj模型是有四个子模块组成的打印的时候它的child有四个,才定义四个的

      this.cord1 = new THREE.Object3D();this.cord2 = new THREE.Object3D();this.cord3 = new THREE.Object3D();this.cord4 = new THREE.Object3D();

接下来:创建一个场景

this.scene = new THREE.Scene();

接下来:在three.js,可以利用THREE.Raycaster来达到点击与交互,即选择物体的操作。点击事件少不了的东西

   this.raycaster = new THREE.Raycaster();

接下来:因为是小白,就给自己加载的模型放了个长500的坐标轴方便观看

   this.scene.add(new THREE.AxesHelper(500));

接下来:得创建相机了,因为不想弄全屏的模型展示,做个800x800的,要是想做全屏的就用window.innerWidth/window.innnerHeight,因为800/800为1,担心不知道是哪个一,第二个参数

  /*** @param {* 初始化相机* }*/this.camera = new THREE.PerspectiveCamera(1,// window.innerWidth / window.innerHeight,800/800,1,10000);

接下来设置相机的位置还是什么东西,自己查查这个api就会有介绍,反正我不懂,瞎设置呗,然后就把相机add到scene(场景)中

    this.camera.position.set(0, 1000, 1000);this.camera.lookAt(new THREE.Vector3(0, 0, 0));// this.camera.lookAt(0, 0, 0);this.scene.add(this.camera);

接下来:既然有点击获取当前的obj模型模块,当然得获取下鼠标的位置吧

   //鼠标的位置this.mouse = new THREE.Vector2();

接下来:渲染器,干什么的?依次的设置是,alpha: true, antialias: true,加了好像清楚一点,然后就是渲染大小当然我的是800x800的全屏就用window.innerWidth, window.innerHeight,渲染显示比例,渲染背景颜色,最后是追加到dom节点

/*** @param {* 渲染器* }*/this.renderer = new THREE.WebGLRenderer({alpha: true,antialias: true,});// this.renderer.setSize(window.innerWidth, window.innerHeight);this.renderer.setSize(800, 800);this.renderer.setPixelRatio(window.devicePixelRatio);this.renderer.setClearColor(0x87ceeb, 1.0);const container = document.getElementById("container");container.appendChild(this.renderer.domElement);

接下来:控制器,不添加模型不能旋转,开始找了好多参数加里面,最后发现还是什么都不加效果控制的更舒坦一点

 /*** @param {* 控制器* }*/this.controls = new OrbitControls(this.camera, this.renderer.domElement);// this.controls.autoRotate=true;// this.controls.target.set(0, 0, 0);// this.controls.minDistance = 80;// this.controls.maxDistance = 400;// this.controls.maxPolarAngle = Math.PI / 3;this.controls.update();

接下来:定义光线,就是有点光打在引入的3d模型上,让模型能正常显示颜色,要不然就是黑白的,参数乱设置的,也没搞懂

  /*** @param {* 定义光线* }*/this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1);// 光线照射的方向this.directionalLight.position.set(0, 1000, 1000).normalize();this.scene.add(this.directionalLight);this.ambient = new THREE.AmbientLight(0xffffff, 1);this.ambient.position.set(0, 0, 0);this.scene.add(this.ambient);

接下来 外部obj,mtl 模型加载方法,碰的坑不亚于点击函数

1.设计人员给了obj mtl 我给放到项目的public/models文件夹下,路径怎么设置?,设置成/models/ssz.obj项目报错,找不到文件,经过查找总算找到了不报错的方法 在return中定义 publicPath: process.env.BASE_URL 在引用路径的时候 mtlLoader.load(${that.publicPath}models/zzs.mtl,function(){})
2.设计人员在做模型的时候用了图片做材质,可是没给我图片包–.--,都是第一次做,采坑
3.设计人员给我材质后还是找不到材质,打开mtl文件一看mtl文件下map_Ka D:\Windo\E\03����.fbm\1.jpg map_Kd D:\Windo\E\03����.fbm\1.jpg 可是在我的电脑完全没有D盘,全部手动修改map_Ka /models/1.jpg map_Kd /models/1.jpg

4.总算引入成功了,可能反正还有一个报错(反正我的报错了)Handlers.get() has been removed. Use LoadingManager.getHandler() instead 错误处理

需要在node_module > three-obj-mtl-loader > index.js中找到第543行并注释掉。在 545行重新定义loader
// var loader = THREE.Loader.Handlers.get( url );var loader = manager.getHandler(url);


在methods中定义的loadObj(加载obj模型)函数代码

loadObj() {let that = this;let objLoader = new OBJLoader();let mtlLoader = new MTLLoader();let textureLoader = new THREE.TextureLoader();// mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) {mtlLoader.load(`${that.publicPath}models/zzs.mtl`,// `${that.publicPath}models/object.mtl`,function (materials) {objLoader.setMaterials(materials);objLoader.load(// `${that.publicPath}models/object.obj`,`${that.publicPath}models/zzs.obj`,function (obj) {obj.scale.multiplyScalar(1);obj.traverse(function (child) {console.log(child.name, "ddddd");// 这里把匹配的模块单独提取出来,如果想让其中一个子模块动// 到时候直接可以在函数外面methed中写一个方法设置,比方说//就单独让m3这个子模块单独动了// that.m3.position.x -= 0.1;// that.m3.position.y -= 0.1;// that.m3.position.z -= 0.1;if (child instanceof THREE.Mesh) {if (child.name == "Box001") {that.m1 = child;} else if (child.name == "Cylinder002") {that.m2 = child;} else if (child.name == "Cylinder003") {//圆柱that.m3 = child;} else if (child.name == "Cylinder004") {//圆柱that.m4 = child;}}});// 这里是做关联,比方说我要移动m4模块,整个模块都会跟着动// 我要移动m3模块,只有m3,m2,m1模块会动that.cord4.add(that.m4);that.cord3.add(that.m3, that.cord4);that.cord2.add(that.m2, that.cord3);that.cord1.add(that.m1, that.cord2);that.scene.add(that.cord1);that.render();return obj;/*** @param {* 将模型加入场景中* }*/// that.scene.add(obj);/*** @param {* 将纹理加入模型中* }*/// that.renderer.render(that.scene, that.camera);},function (xhr) {console.log((xhr.loaded / xhr.total) * 100 + "% loaded");},function (error) {console.log("An error happened");});});},

接下来:添加窗口监听事件,固定宽高的就不用了了吧,800x800监听window窗口大小不还是800x800展示么,不过像是用window.innerWidth做长window.innerHeight做宽全屏展示的还是要监听的

window.addEventListener("resize", this.onWindowResize, false);

然后这是this.onWindowResize的方法,卸载methods中就行

 /*** @param {* 窗口监听函数* }*/onWindowResize() {this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();this.renderer.setSize(window.innerWidth, window.innerHeight);},

接下来:点击总得有个点击事件,
一、这个是800x800固定宽高的点击函数写法,因为不需要监听点击全屏啊,监听当前的dom容器就行了

this.$refs.container.addEventListener("click", this.clickTest, false);

二、这个是设置长window.innerWidth宽window.innerHeight的写法

window.addEventListener("click", this.clickTest, false);

三、点击函数方法:在methods中定义clickTest方法
开始只知道全屏的点击可以点击到模型本身并不知道800x800这种自定义宽高的怎么点击到当前obj模型,总是匹配不到,全网都没找到这个方法,可能全网就我是最笨的,
window.innerWidth x window.innerHeight全屏大小的写法

this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

800x800写法:因为监听的是当前的dom元素的点击区域,发现event对象中有layerX 和layerY 两个参数非常可疑,正是鼠标点击dom元素上在dom元素自身上的坐标,参考上面全屏写法所以就懂了。
this.raycaster.intersectObjects(this.objects);是判断当前点击的位置与照相机的位置相链接成一条直线,哪些模型会在这条线上,返回一个数组,这个数组如果有值,第[0]位就是最上面的,也就是所谓点击的这么模型中的子模块

       this.mouse.x = (event.layerX / 800) * 2 - 1;this.mouse.y = -(event.layerY / 800) * 2 + 1;
    //点击事件clickTest(event) {console.log(event)event.preventDefault();this.objects = [];// this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;this.mouse.x = (event.layerX / 800) * 2 - 1;this.mouse.y = -(event.layerY / 800) * 2 + 1;// this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;this.raycaster.setFromCamera(this.mouse, this.camera);this.scene.children.forEach((child) => {//根据需求判断哪些加入objects,也可以在生成object的时候push进objectsif (!(child instanceof THREE.GridHelper) &&!(child instanceof THREE.DirectionalLight) &&!(child instanceof THREE.AmbientLight)) {child.children.forEach((childv) => {if (childv instanceof THREE.Mesh) {//根据需求判断哪些加入objects,也可以在生成object的时候push进objectsthis.objects.push(childv);} else if (childv instanceof THREE.Object3D) {this.meshDg(childv.children);}});}});console.log(this.objects, 6666666 + "obj");var intersects = this.raycaster.intersectObjects(this.objects);console.log(intersects, 8888 + "intersects");if (intersects.length != 0 &&intersects[0].object instanceof THREE.Mesh) {this.selectObject = intersects[0].object;this.changeMaterial(this.selectObject);console.log(this.selectObject.name);// $("#alert_center").css({//   display: "block",// });// $("#alert_font").html(//   "当前点击的模块名字是:" + this.selectObject.name// );this.render();}},

点击事件,点击后改变当前对象的方法:在methods中定义,点击了给它变个颜色看看,纯属个人无聊

    /*** @param {* 改变当前对象属性* }*/changeMaterial(object) {var material = new THREE.MeshLambertMaterial({color: 0xffffff * Math.random(),});object.material = material;},

动画:写完后面,就忘了前面,写着不报错无关痛痒,在methods中定义

    /*** @param {* 动画* }*/animate() {requestAnimationFrame(this.animate);this.render();},

渲染:忘了具体干嘛的,应该少不了:在methos中定义

 /*** @param {* 渲染* }*/render() {this.renderer.render(this.scene, this.camera);},

下面代码复制下来也跑不了的,模型什么的都需要更改

   <template><div class="spring"><!-- <div class="box"></div> --><div id="container" ref="container"></div></div>
</template>
<script>
import * as THREE from "three";
import { OBJLoader, MTLLoader } from "three-obj-mtl-loader";
import { CSS2DRenderer, CSS2DObject } from "three-css2drender";
const OrbitControls = require("three-orbit-controls")(THREE);
export default {name: "",components: {},props: {},data() {return {axes: "",group: "",scene: "",light: "",camera: "",controls: "",renderer: "",directionalLight: "",ambient: "",loader: "", //地板glass_material: "",publicPath: process.env.BASE_URL,m1: "",m2: "",m3: "",mouse: "",objects: [],raycaster: "",getBoundingClientRect: {},offsetWidth: "",offsetHeight: "",theta: 0, //相机旋转角度cord1: {},cord2: {},cord3: {},cord4: {},selectObject: {}, //被点击的第一个元素};},created() {},mounted() {this.init();this.loadObj();this.animate();},methods: {/*** @param {* 初始化three.js相关内容}*/init() {/*** @param {* 场景* }*/this.cord1 = new THREE.Object3D();this.cord2 = new THREE.Object3D();this.cord3 = new THREE.Object3D();this.cord4 = new THREE.Object3D();this.scene = new THREE.Scene();this.raycaster = new THREE.Raycaster();/*** @param {* 世界坐标系* }*/this.scene.add(new THREE.AxesHelper(500));// var helper = new THREE.GridHelper(3000, 50, 0xcd3700, 0x4a4a4a);// this.scene.add(helper);/*** @param {* 初始化相机* }*/this.camera = new THREE.PerspectiveCamera(1,// window.innerWidth / window.innerHeight,1,1,10000);this.camera.position.set(0, 1000, 1000);this.camera.lookAt(new THREE.Vector3(0, 0, 0));// this.camera.lookAt(0, 0, 0);this.scene.add(this.camera);//鼠标的位置this.mouse = new THREE.Vector2();/*** @param {* 渲染器* }*/this.renderer = new THREE.WebGLRenderer({alpha: true,antialias: true,});// this.renderer.setSize(window.innerWidth, window.innerHeight);this.renderer.setSize(800, 800);this.renderer.setPixelRatio(window.devicePixelRatio);this.renderer.setClearColor(0x87ceeb, 1.0);const container = document.getElementById("container");container.appendChild(this.renderer.domElement);/*** @param {* 获取当前dom元素坐标* }*/this.getBoundingClientRect = this.$refs.container.getBoundingClientRect();this.offsetWidth = this.$refs.container.offsetWidth;this.offsetHeight = this.$refs.container.offsetHeight;/*** @param {* 控制器* }*/this.controls = new OrbitControls(this.camera, this.renderer.domElement);this.controls.update();/*** @param {* 定义光线* }*/this.directionalLight = new THREE.DirectionalLight(0xc1c1c1, 1);// 光线照射的方向this.directionalLight.position.set(0, 1000, 1000).normalize();this.scene.add(this.directionalLight);this.ambient = new THREE.AmbientLight(0xffffff, 1);this.ambient.position.set(0, 0, 0);this.scene.add(this.ambient);/*** @param {* 添加窗口监听事件(resize-onresize即窗口或框架被重新调整大小)* }*/this.$refs.container.addEventListener("click", this.clickTest, false);// window.addEventListener("resize", this.onWindowResize, false);},/*** @param {* 外部模型加载函数* }*/loadObj() {let that = this;let objLoader = new OBJLoader();let mtlLoader = new MTLLoader();let textureLoader = new THREE.TextureLoader();// mtlLoader.load(`${that.publicPath}models/zzs.mtl`, function (materials) {mtlLoader.load(`${that.publicPath}models/zzs.mtl`,// `${that.publicPath}models/object.mtl`,function (materials) {objLoader.setMaterials(materials);objLoader.load(// `${that.publicPath}models/object.obj`,`${that.publicPath}models/zzs.obj`,function (obj) {obj.scale.multiplyScalar(1);obj.traverse(function (child) {console.log(child.name,'ddddd')if (child instanceof THREE.Mesh) {if (child.name == "Box001") {that.m1 = child;} else if (child.name == "Cylinder002") {that.m2 = child;} else if (child.name == "Cylinder003") {that.m3 = child;}else if (child.name == "Cylinder004") {that.m4 = child;}}});that.cord4.add(that.m4);that.cord3.add(that.m3, that.cord4);// console.log(999);// that.cord3.add(that.m3);that.cord2.add(that.m2, that.cord3);that.cord1.add(that.m1, that.cord2);that.scene.add(that.cord1);that.render();return obj;},function (xhr) {console.log((xhr.loaded / xhr.total) * 100 + "% loaded");},function (error) {console.log("An error happened");});});},//递归  收集  THREE.MeshmeshDg(child) {child.forEach((childv) => {if (childv instanceof THREE.Mesh) {//根据需求判断哪些加入objects,也可以在生成object的时候push进objectsthis.objects.push(childv);} else if (childv instanceof THREE.Object3D) {this.meshDg(childv.children);}});},//点击事件clickTest(event) {console.log(event)event.preventDefault();this.objects = [];// this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1;this.mouse.x = (event.layerX / 800) * 2 - 1;this.mouse.y = -(event.layerY / 800) * 2 + 1;// this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;this.raycaster.setFromCamera(this.mouse, this.camera);this.scene.children.forEach((child) => {//根据需求判断哪些加入objects,也可以在生成object的时候push进objectsif (!(child instanceof THREE.GridHelper) &&!(child instanceof THREE.DirectionalLight) &&!(child instanceof THREE.AmbientLight)) {child.children.forEach((childv) => {if (childv instanceof THREE.Mesh) {//根据需求判断哪些加入objects,也可以在生成object的时候push进objectsthis.objects.push(childv);} else if (childv instanceof THREE.Object3D) {this.meshDg(childv.children);}});}});console.log(this.objects, 6666666 + "obj");var intersects = this.raycaster.intersectObjects(this.objects);console.log(intersects, 8888 + "intersects");if (intersects.length != 0 &&intersects[0].object instanceof THREE.Mesh) {this.selectObject = intersects[0].object;this.changeMaterial(this.selectObject);console.log(this.selectObject.name);//这里找的代码,用到了jq,用vue就更简单了// $("#alert_center").css({//   display: "block",// });// $("#alert_font").html(//   "当前点击的模块名字是:" + this.selectObject.name// );this.render();}},/*** @param {* 改变当前对象属性* }*/changeMaterial(object) {var material = new THREE.MeshLambertMaterial({color: 0xffffff * Math.random(),});object.material = material;},/*** @param {* 动画* }*/animate() {requestAnimationFrame(this.animate);this.render();},/*** @param {* 渲染* }*/render() {this.renderer.render(this.scene, this.camera);},/*** @param {* 窗口监听函数* }*/onWindowResize() {this.camera.aspect = window.innerWidth / window.innerHeight;this.camera.updateProjectionMatrix();// this.renderer.setSize(window.innerWidth, window.innerHeight);this.renderer.setSize(800, 800);},},computed: {},
};
</script>
<style lang='scss' scoped>
.spring {width: 100%;height: 100%;display: flex;flex-direction: column;justify-content: center;align-items: center;
}
.box {width: 100%;height: 100px;
}
</style>

three、vue中使用three、three怎么加载obj模型和mtl文件、three自定义800*800大小怎么拾取/点击相关推荐

  1. 自上而下解读ncnn系列(1):加载param模型和bin文件前向传播

    由于这段时间着手实现tensorflow到ncnn的转换,开发过程中对ncnn框架有了一定的认识,特此分享. 关于tensorflow2ncnn的具体细节和步骤,可以参考我的github: https ...

  2. html5 语音包,在vue中使用vue-i18n按需加载语言包

    1.新建目录结构如下: . ├── App.vue ├── assets │   └── langs │ ├── en │ │ └── index.js │   ├── zh │ │ └── inde ...

  3. vue spa php,在Vue中有关SPA首屏加载优化(详细教程)

    本篇文章主要介绍了浅谈Vue SPA 首屏加载优化实践,小编觉得挺不错的,现在分享给大家,也给大家做个参考.一起跟随小编过来看看吧 写在前面 本文记录笔者在Vue SPA项目首屏加载优化过程中遇到的一 ...

  4. VUE中实现三维地图Cesium加载全国地质管地质地图

    1.Vue中使用脚手架npm安装Cesium,安装命令:npm install cesium,加载三维地图Cesium,下载Cesium相关文件,安装成功后如下图所示: <div id=&quo ...

  5. Vue中实现特效下拉加载更多数据

    1.功能需求 其实在很多的页面开发过程中,有些页面尤其是评论页面,在第一次加载的时候并不会加载很多的相关数据,而是加载一部分,当用户下拉旁边的滚动条时,尤其是滚动条移动到底部的时候,就会出现行的相关内 ...

  6. unity动态加载obj文件

    unity2018.4.2f1 vs2017 最近项目需求,需要实现动态读物外部obj模型,并加载到场景中,研究了好几天,终于实现了,在此做个记录. 1.首先随便找个.obj模型,带贴图,我的资源截图 ...

  7. Java3D加载obj文件+mtl文件

    Java3d入门学习可以参考这位博主大神--苏若年,关于Java3D学习的文章.下面给出他部分文章的链接: 文1 创建三维几何模型:[ http://www.cnblogs.com/dennisit/ ...

  8. Virtualbox 如何安装增强功能,加载VBoxGuestAdditions光盘映像文件

    1.首先,在网上下载VBoxGuestAdditions光盘映像文件 下载地址: http://download.virtualbox.org/virtualbox/ 2.在VirtualBox的存储 ...

  9. 上拉加载_如何用Vue + Mint UI实现上拉加载更多

    引言: 上拉加载更多在移动端不论是在 app 里面还是在页面中都是必不可少的,以下是 mint-ui 中上拉加载更多的总结. 一.在项目中使用 mint-ui 需要先安装 查看官网 (1)安装:npm ...

最新文章

  1. C程序设计-----第1次作业
  2. 微信小程序 侧滑效果实现
  3. 查看node状态_第六章 无限可能,神器降临——Node-RED
  4. 网易企业业务进入大航海时代,邀您共创星辰大海
  5. CCIE理论-第十五篇-IPV6-重分布+ACL+前缀列表
  6. 架构设计 | 基于电商交易流程,图解TCC事务分段提交
  7. excanvas让canvas兼容ie7,8
  8. react网页适配不同分辨率_PC端页面适应不同的分辨率的方法 (转载)
  9. 调用赋码远程服务异常_Remoting远程访问的这个异常怎么处理???
  10. python数学实验与建模司守奎pdf_数学建模算法与程序司守奎.pdf
  11. 模拟银行转账(java+mysql+tomcat +JDBC+ druid连接池 + Servlet + Ajax)
  12. 使用什么协议扫描服务器端口,服务器端口扫描
  13. 盲文压纹机和AAC设备
  14. 第六章:详细设计。盒图、问题分析图即PAD图、过程设计语言PDL伪码
  15. 计算机专业评测绘工程师,如何成为测绘工程师
  16. 图像工程的读书笔记 图像成像过程
  17. 图解电动汽车:电动汽车的传感器
  18. C++ 中的万能开头
  19. linux虚拟IP/yum Invalid version flag: if 错误//configure:错误:HTTP重写模块需要PCRE库。
  20. 计算机学院算法实验报告,四川大学计算机学院数据结构与算法分析实验报告

热门文章

  1. 为什么我的pycharm创建不了python_[新手向视频]新版PyCharm创建项目为什么会有问题...
  2. VS2017+VUE创建项目爬坑
  3. 如果一觉醒来已是光年之远
  4. 硬盘分几个区最好?硬盘分区和库的关系
  5. STM32F4---通用定时器更新中断
  6. electron 设置窗口默认最大化、全屏
  7. 透彻理解SLAM中的非线性最小二乘问题
  8. 单页面系统知识点记录
  9. 计算机二级投影运算怎么看,二级计算机中交、并、除、自然连接、投影、选择和笛卡尔积是怎么计算的?...
  10. 离散信道容量迭代算法